diff options
Diffstat (limited to 'src')
10 files changed, 964 insertions, 1 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 257f0dfcf..6395d0671 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -40,6 +40,7 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.ActivityOptions; +import android.app.KeyguardManager; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.content.ActivityNotFoundException; @@ -287,6 +288,11 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private DeviceProfile mStableDeviceProfile; private RotationMode mRotationMode = RotationMode.NORMAL; + private static final int REQUEST_AUTH_CODE = 93; + private View mAuthView; + private ItemInfo mAuthInfo; + private String mAuthContainer; + @Override protected void onCreate(Bundle savedInstanceState) { RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER); @@ -682,6 +688,13 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } }; + if (requestCode == REQUEST_AUTH_CODE) { + if (resultCode == RESULT_OK) { + startActivitySafely(mAuthView, requestArgs.getPendingIntent(), mAuthInfo, mAuthContainer); + } + return; + } + if (requestCode == REQUEST_BIND_APPWIDGET) { // This is called only if the user did not previously have permissions to bind widgets final int appWidgetId = data != null ? @@ -1002,6 +1015,31 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mStateManager.onWindowFocusChanged(); } + public void startActivitySafelyAuth(View v, Intent intent, ItemInfo item, + String sourceContainer) { + KeyguardManager manager = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? + getSystemService(KeyguardManager.class) : + (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); + if (manager == null) { + throw new NullPointerException("No KeyguardManager found!"); + } + + String title = getString(R.string.trust_apps_manager_name); + String message = getString(R.string.trust_apps_auth_open_app, item.title); + Intent kmIntent = manager.createConfirmDeviceCredentialIntent(title, message); + + if (kmIntent != null) { + mAuthView = v; + mAuthInfo = item; + mAuthContainer = sourceContainer; + setWaitingForResult(PendingRequestArgs.forIntent(REQUEST_AUTH_CODE, intent, item)); + startActivityForResult(kmIntent, REQUEST_AUTH_CODE); + return; + } + + startActivitySafely(v, intent, item, sourceContainer); + } + public interface LauncherOverlay { /** diff --git a/src/com/android/launcher3/lineage/trust/HiddenAppsFilter.java b/src/com/android/launcher3/lineage/trust/HiddenAppsFilter.java new file mode 100644 index 000000000..942887693 --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/HiddenAppsFilter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.lineage.trust; + +import android.content.ComponentName; +import android.content.Context; + +import com.android.launcher3.AppFilter; +import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper; + +@SuppressWarnings("unused") +public class HiddenAppsFilter extends AppFilter { + private TrustDatabaseHelper mDbHelper; + + public HiddenAppsFilter(Context context) { + if (context == null) { + throw new IllegalArgumentException("Context must not be null!"); + } + + mDbHelper = TrustDatabaseHelper.getInstance(context); + } + + @Override + public boolean shouldShowApp(ComponentName app) { + return !mDbHelper.isPackageHidden(app.getPackageName()); + } +} diff --git a/src/com/android/launcher3/lineage/trust/LoadTrustComponentsTask.java b/src/com/android/launcher3/lineage/trust/LoadTrustComponentsTask.java new file mode 100644 index 000000000..bf0a08d49 --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/LoadTrustComponentsTask.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.lineage.trust; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Build; + +import androidx.annotation.NonNull; + +import com.android.launcher3.lineage.trust.db.TrustComponent; +import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class LoadTrustComponentsTask extends AsyncTask<Void, Integer, List<TrustComponent>> { + @NonNull + private TrustDatabaseHelper mDbHelper; + + @NonNull + private PackageManager mPackageManager; + + @NonNull + private Callback mCallback; + + LoadTrustComponentsTask(@NonNull TrustDatabaseHelper dbHelper, + @NonNull PackageManager packageManager, + @NonNull Callback callback) { + mDbHelper = dbHelper; + mPackageManager = packageManager; + mCallback = callback; + } + + @Override + protected List<TrustComponent> doInBackground(Void... voids) { + List<TrustComponent> list = new ArrayList<>(); + + Intent filter = new Intent(Intent.ACTION_MAIN, null); + filter.addCategory(Intent.CATEGORY_LAUNCHER); + + List<ResolveInfo> apps = mPackageManager.queryIntentActivities(filter, + PackageManager.GET_META_DATA); + + int numPackages = apps.size(); + for (int i = 0; i < numPackages; i++) { + ResolveInfo app = apps.get(i); + try { + String pkgName = app.activityInfo.packageName; + String label = mPackageManager.getApplicationLabel( + mPackageManager.getApplicationInfo(pkgName, + PackageManager.GET_META_DATA)).toString(); + Drawable icon = app.loadIcon(mPackageManager); + boolean isHidden = mDbHelper.isPackageHidden(pkgName); + boolean isProtected = mDbHelper.isPackageProtected(pkgName); + + list.add(new TrustComponent(pkgName, icon, label, isHidden, isProtected)); + + publishProgress(Math.round(i * 100f / numPackages)); + } catch (PackageManager.NameNotFoundException ignored) { + } + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Collections.sort(list, (a, b) -> a.getLabel().compareTo(b.getLabel())); + } + + return list; + } + + @Override + protected void onProgressUpdate(Integer... values) { + if (values.length > 0) { + mCallback.onLoadListProgress(values[0]); + } + } + + @Override + protected void onPostExecute(List<TrustComponent> trustComponents) { + mCallback.onLoadCompleted(trustComponents); + } + + interface Callback { + void onLoadListProgress(int progress); + void onLoadCompleted(List<TrustComponent> result); + } +} diff --git a/src/com/android/launcher3/lineage/trust/TrustAppsActivity.java b/src/com/android/launcher3/lineage/trust/TrustAppsActivity.java new file mode 100644 index 000000000..cd50af968 --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/TrustAppsActivity.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.lineage.trust; + +import android.app.ActionBar; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.KeyguardManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Build; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.lineage.trust.db.TrustComponent; +import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper; + +import java.util.List; + +import static com.android.launcher3.lineage.trust.db.TrustComponent.Kind.HIDDEN; +import static com.android.launcher3.lineage.trust.db.TrustComponent.Kind.PROTECTED; + +public class TrustAppsActivity extends Activity implements + TrustAppsAdapter.Listener, + LoadTrustComponentsTask.Callback, + UpdateItemTask.UpdateCallback { + + private static final int REQUEST_AUTH_CODE = 92; + private static final String KEY_TRUST_ONBOARDING = "pref_trust_onboarding"; + + private RecyclerView mRecyclerView; + private LinearLayout mLoadingView; + private ProgressBar mProgressBar; + + private TrustDatabaseHelper mDbHelper; + private TrustAppsAdapter mAdapter; + + @Override + protected void onCreate(@Nullable Bundle savedInstance) { + super.onCreate(savedInstance); + + ActionBar actionBar = getActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + } + + setContentView(R.layout.activity_hidden_apps); + mRecyclerView = findViewById(R.id.hidden_apps_list); + mLoadingView = findViewById(R.id.hidden_apps_loading); + mProgressBar = findViewById(R.id.hidden_apps_progress_bar); + + mAdapter = new TrustAppsAdapter(this); + mDbHelper = TrustDatabaseHelper.getInstance(this); + + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mRecyclerView.setItemAnimator(new DefaultItemAnimator()); + mRecyclerView.setAdapter(mAdapter); + + authenticate(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_AUTH_CODE) { + if (resultCode == Activity.RESULT_OK) { + showUi(); + } else { + finish(); + } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater menuInflater = getMenuInflater(); + menuInflater.inflate(R.menu.menu_trust_apps, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == android.R.id.home) { + finish(); + return true; + } else if (id == R.id.menu_trust_help) { + showOnBoarding(true); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onHiddenItemChanged(@NonNull TrustComponent component) { + new UpdateItemTask(mDbHelper, this, HIDDEN).execute(component); + } + + @Override + public void onProtectedItemChanged(@NonNull TrustComponent component) { + new UpdateItemTask(mDbHelper, this, PROTECTED).execute(component); + } + + @Override + public void onUpdated(boolean result) { + LauncherAppState state = LauncherAppState.getInstanceNoCreate(); + if (state != null) { + state.getModel().forceReload(); + } + } + + @Override + public void onLoadListProgress(int progress) { + mProgressBar.setProgress(progress); + } + + @Override + public void onLoadCompleted(List<TrustComponent> result) { + mLoadingView.setVisibility(View.GONE); + mRecyclerView.setVisibility(View.VISIBLE); + mAdapter.update(result); + } + + private void authenticate() { + KeyguardManager manager = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? + getSystemService(KeyguardManager.class) : + (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); + if (manager == null) { + throw new NullPointerException("No KeyguardManager found!"); + } + + String title = getString(R.string.trust_apps_manager_name); + String message = getString(R.string.trust_apps_auth_manager); + Intent intent = manager.createConfirmDeviceCredentialIntent(title, message); + + if (intent != null) { + startActivityForResult(intent, REQUEST_AUTH_CODE); + return; + } + + Toast.makeText(this, R.string.trust_apps_no_lock_error, + Toast.LENGTH_LONG).show(); + finish(); + } + + private void showUi() { + mLoadingView.setVisibility(View.VISIBLE); + + showOnBoarding(false); + + new LoadTrustComponentsTask(mDbHelper, getPackageManager(), this).execute(); + } + + private void showOnBoarding(boolean forceShow) { + SharedPreferences preferenceManager = Utilities.getPrefs(this); + if (!forceShow && preferenceManager.getBoolean(KEY_TRUST_ONBOARDING, false)) { + return; + } + + preferenceManager.edit() + .putBoolean(KEY_TRUST_ONBOARDING, true) + .apply(); + + new AlertDialog.Builder(this) + .setView(R.layout.dialog_trust_welcome) + .setPositiveButton(android.R.string.ok, null) + .show(); + } +} diff --git a/src/com/android/launcher3/lineage/trust/TrustAppsAdapter.java b/src/com/android/launcher3/lineage/trust/TrustAppsAdapter.java new file mode 100644 index 000000000..3d57dc5c4 --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/TrustAppsAdapter.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.lineage.trust; + +import android.graphics.drawable.Animatable2; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.launcher3.R; +import com.android.launcher3.lineage.trust.db.TrustComponent; + +import java.util.ArrayList; +import java.util.List; + +class TrustAppsAdapter extends RecyclerView.Adapter<TrustAppsAdapter.ViewHolder> { + private List<TrustComponent> mList = new ArrayList<>(); + private Listener mListener; + + TrustAppsAdapter(Listener listener) { + mListener = listener; + } + + public void update(List<TrustComponent> list) { + DiffUtil.DiffResult result = DiffUtil.calculateDiff(new Callback(mList, list)); + mList = list; + result.dispatchUpdatesTo(this); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) { + return new ViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_hidden_app, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) { + viewHolder.bind(mList.get(i)); + } + + @Override + public int getItemCount() { + return mList.size(); + } + + public interface Listener { + void onHiddenItemChanged(@NonNull TrustComponent component); + + void onProtectedItemChanged(@NonNull TrustComponent component); + } + + class ViewHolder extends RecyclerView.ViewHolder { + private ImageView mIconView; + private TextView mLabelView; + private ImageView mHiddenView; + private ImageView mProtectedView; + + ViewHolder(@NonNull View itemView) { + super(itemView); + + mIconView = itemView.findViewById(R.id.item_hidden_app_icon); + mLabelView = itemView.findViewById(R.id.item_hidden_app_title); + mHiddenView = itemView.findViewById(R.id.item_hidden_app_switch); + mProtectedView = itemView.findViewById(R.id.item_protected_app_switch); + } + + void bind(TrustComponent component) { + mIconView.setImageDrawable(component.getIcon()); + mLabelView.setText(component.getLabel()); + + mHiddenView.setImageResource(component.isHidden() ? + R.drawable.ic_hidden_locked : R.drawable.ic_hidden_unlocked); + mProtectedView.setImageResource(component.isProtected() ? + R.drawable.ic_protected_locked : R.drawable.ic_protected_unlocked); + + mHiddenView.setOnClickListener(v -> { + component.invertVisibility(); + + mHiddenView.setImageResource(component.isHidden() ? + R.drawable.avd_hidden_lock : R.drawable.avd_hidden_unlock); + AnimatedVectorDrawable avd = (AnimatedVectorDrawable) mHiddenView.getDrawable(); + + int position = getAdapterPosition(); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + avd.registerAnimationCallback(new Animatable2.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + updateHiddenList(position, component); + } + }); + avd.start(); + } else { + avd.start(); + updateHiddenList(position, component); + } + }); + + mProtectedView.setOnClickListener(v -> { + component.invertProtection(); + + mProtectedView.setImageResource(component.isProtected() ? + R.drawable.avd_protected_lock : R.drawable.avd_protected_unlock); + AnimatedVectorDrawable avd = (AnimatedVectorDrawable) mProtectedView.getDrawable(); + + int position = getAdapterPosition(); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + avd.registerAnimationCallback(new Animatable2.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + updateProtectedList(position, component); + } + }); + avd.start(); + } else { + avd.start(); + updateProtectedList(position, component); + } + }); + } + + private void updateHiddenList(int position, TrustComponent component) { + mListener.onHiddenItemChanged(component); + updateList(position, component); + } + + private void updateProtectedList(int position, TrustComponent component) { + mListener.onProtectedItemChanged(component); + updateList(position, component); + } + + private void updateList(int position, TrustComponent component) { + mList.set(position, component); + notifyItemChanged(position); + } + } + + private static class Callback extends DiffUtil.Callback { + List<TrustComponent> mOldList; + List<TrustComponent> mNewList; + + public Callback(List<TrustComponent> oldList, + List<TrustComponent> newList) { + mOldList = oldList; + mNewList = newList; + } + + + @Override + public int getOldListSize() { + return mOldList.size(); + } + + @Override + public int getNewListSize() { + return mNewList.size(); + } + + @Override + public boolean areItemsTheSame(int iOld, int iNew) { + String oldPkg = mOldList.get(iOld).getPackageName(); + String newPkg = mNewList.get(iNew).getPackageName(); + return oldPkg.equals(newPkg); + } + + @Override + public boolean areContentsTheSame(int iOld, int iNew) { + return mOldList.get(iOld).equals(mNewList.get(iNew)); + } + } +} diff --git a/src/com/android/launcher3/lineage/trust/UpdateItemTask.java b/src/com/android/launcher3/lineage/trust/UpdateItemTask.java new file mode 100644 index 000000000..26ae24c6c --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/UpdateItemTask.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.lineage.trust; + +import android.os.AsyncTask; +import androidx.annotation.NonNull; + +import com.android.launcher3.lineage.trust.db.TrustComponent; +import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper; + +public class UpdateItemTask extends AsyncTask<TrustComponent, Void, Boolean> { + @NonNull + private TrustDatabaseHelper mDbHelper; + @NonNull + private UpdateCallback mCallback; + @NonNull + private TrustComponent.Kind mKind; + + UpdateItemTask(@NonNull TrustDatabaseHelper dbHelper, + @NonNull UpdateCallback callback, + @NonNull TrustComponent.Kind kind) { + mDbHelper = dbHelper; + mCallback = callback; + mKind = kind; + } + + @Override + protected Boolean doInBackground(TrustComponent... trustComponents) { + if (trustComponents.length < 1) { + return false; + } + + TrustComponent component = trustComponents[0]; + String pkgName = component.getPackageName(); + + switch (mKind) { + case HIDDEN: + if (component.isHidden()) { + mDbHelper.addHiddenApp(pkgName); + } else { + mDbHelper.removeHiddenApp(pkgName); + } + break; + case PROTECTED: + if (component.isProtected()) { + mDbHelper.addProtectedApp(pkgName); + } else { + mDbHelper.removeProtectedApp(pkgName); + } + break; + } + return true; + } + + @Override + protected void onPostExecute(Boolean result) { + mCallback.onUpdated(result); + } + + interface UpdateCallback { + void onUpdated(boolean result); + } +} diff --git a/src/com/android/launcher3/lineage/trust/db/TrustComponent.java b/src/com/android/launcher3/lineage/trust/db/TrustComponent.java new file mode 100644 index 000000000..5342bde3d --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/db/TrustComponent.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.lineage.trust.db; + +import android.graphics.drawable.Drawable; +import androidx.annotation.NonNull; + +public class TrustComponent { + @NonNull + private final String mPackageName; + @NonNull + private final Drawable mIcon; + @NonNull + private final String mLabel; + + private boolean mIsHidden; + private boolean mIsProtected; + + public TrustComponent(@NonNull String packageName, @NonNull Drawable icon, + @NonNull String label, boolean isHidden, boolean isProtected) { + mPackageName = packageName; + mIcon = icon; + mLabel = label; + mIsHidden = isHidden; + mIsProtected = isProtected; + } + + @NonNull + public String getPackageName() { + return mPackageName; + } + + @NonNull + public Drawable getIcon() { + return mIcon; + } + + @NonNull + public String getLabel() { + return mLabel; + } + + public boolean isHidden() { + return mIsHidden; + } + + public boolean isProtected() { + return mIsProtected; + } + + public void invertVisibility() { + mIsHidden = !mIsHidden; + } + + public void invertProtection() { + mIsProtected = !mIsProtected; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof TrustComponent)) { + return false; + } + + TrustComponent otherComponent = (TrustComponent) other; + return otherComponent.getPackageName().equals(mPackageName) && + otherComponent.isHidden() == mIsHidden; + } + + @Override + public int hashCode() { + return mPackageName.hashCode() + (mIsHidden ? 1 : 0); + } + + public enum Kind { + HIDDEN, + PROTECTED, + } +} diff --git a/src/com/android/launcher3/lineage/trust/db/TrustDatabaseHelper.java b/src/com/android/launcher3/lineage/trust/db/TrustDatabaseHelper.java new file mode 100644 index 000000000..715355c8c --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/db/TrustDatabaseHelper.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.lineage.trust.db; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class TrustDatabaseHelper extends SQLiteOpenHelper { + private static final int DATABASE_VERSION = 1; + private static final String DATABASE_NAME = "trust_apps_db"; + + private static final String TABLE_NAME = "trust_apps"; + private static final String KEY_UID = "uid"; + private static final String KEY_PKGNAME = "pkgname"; + private static final String KEY_HIDDEN = "hidden"; + private static final String KEY_PROTECTED = "protected"; + + @Nullable + private static TrustDatabaseHelper sSingleton; + + private TrustDatabaseHelper(@NonNull Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + public static synchronized TrustDatabaseHelper getInstance(@NonNull Context context) { + if (sSingleton == null) { + sSingleton = new TrustDatabaseHelper(context); + } + + return sSingleton; + } + + @Override + public void onCreate(SQLiteDatabase db) { + String CMD_CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + + "(" + + KEY_UID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + KEY_PKGNAME + " TEXT," + + KEY_HIDDEN + " INTEGER DEFAULT 0," + + KEY_PROTECTED + " INTEGER DEFAULT 0" + + ")"; + db.execSQL(CMD_CREATE_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + } + + public void addHiddenApp(@NonNull String packageName) { + if (isPackageHidden(packageName)) { + return; + } + + SQLiteDatabase db = getWritableDatabase(); + db.beginTransaction(); + + try { + ContentValues values = new ContentValues(); + values.put(KEY_PKGNAME, packageName); + values.put(KEY_HIDDEN, 1); + + int rows = db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?", new String[]{KEY_PKGNAME}); + if (rows != 1) { + // Entry doesn't exist, create a new one + db.insertOrThrow(TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } catch (Exception e) { + // Ignored + } finally { + db.endTransaction(); + } + } + + public void addProtectedApp(@NonNull String packageName) { + if (isPackageProtected(packageName)) { + return; + } + + SQLiteDatabase db = getWritableDatabase(); + db.beginTransaction(); + + try { + ContentValues values = new ContentValues(); + values.put(KEY_PKGNAME, packageName); + values.put(KEY_PROTECTED, 1); + + int rows = db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?", new String[]{KEY_PKGNAME}); + if (rows != 1) { + // Entry doesn't exist, create a new one + db.insertOrThrow(TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } catch (Exception e) { + // Ignored + } finally { + db.endTransaction(); + } + } + + + public void removeHiddenApp(@NonNull String packageName) { + if (!isPackageHidden(packageName)) { + return; + } + + SQLiteDatabase db = getWritableDatabase(); + db.beginTransaction(); + + try { + ContentValues values = new ContentValues(); + values.put(KEY_HIDDEN, 0); + + db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?", new String[]{packageName}); + db.setTransactionSuccessful(); + } catch (Exception e) { + // Ignored + } finally { + db.endTransaction(); + } + } + + public void removeProtectedApp(@NonNull String packageName) { + if (!isPackageProtected(packageName)) { + return; + } + + SQLiteDatabase db = getWritableDatabase(); + db.beginTransaction(); + + try { + ContentValues values = new ContentValues(); + values.put(KEY_PROTECTED, 0); + + db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?", new String[]{packageName}); + db.setTransactionSuccessful(); + } catch (Exception e) { + // Ignored + } finally { + db.endTransaction(); + } + } + + public boolean isPackageHidden(@NonNull String packageName) { + String query = String.format("SELECT * FROM %s WHERE %s = ? AND %s = ?", TABLE_NAME, KEY_PKGNAME, KEY_HIDDEN); + SQLiteDatabase db = getReadableDatabase(); + Cursor cursor = db.rawQuery(query, new String[]{packageName, String.valueOf(1)}); + boolean result = false; + try { + result = cursor.getCount() != 0; + } catch (Exception e) { + // Ignored + } finally { + if (cursor != null && !cursor.isClosed()) { + cursor.close(); + } + } + + return result; + } + + public boolean isPackageProtected(@NonNull String packageName) { + String query = String.format("SELECT * FROM %s WHERE %s = ? AND %s = ?", TABLE_NAME, KEY_PKGNAME, KEY_PROTECTED); + SQLiteDatabase db = getReadableDatabase(); + Cursor cursor = db.rawQuery(query, new String[]{packageName, String.valueOf(1)}); + boolean result = false; + try { + result = cursor.getCount() != 0; + } catch (Exception e) { + // Ignored + } finally { + if (cursor != null && !cursor.isClosed()) { + cursor.close(); + } + } + + return result; + } +} diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java index 7c38cacdc..be8aed2cf 100644 --- a/src/com/android/launcher3/settings/SettingsActivity.java +++ b/src/com/android/launcher3/settings/SettingsActivity.java @@ -25,6 +25,7 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.DialogFragment; import android.app.Fragment; +import android.content.Intent; import android.os.Bundle; import android.provider.Settings; import android.text.TextUtils; @@ -35,6 +36,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.lineage.LineageLauncherCallbacks; import com.android.launcher3.lineage.LineageUtils; +import com.android.launcher3.lineage.trust.TrustAppsActivity; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.SecureSettingsObserver; @@ -65,6 +67,7 @@ public class SettingsActivity extends Activity public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted"; public static final String KEY_MINUS_ONE = "pref_enable_minus_one"; + public static final String KEY_TRUST_APPS = "pref_trust_apps"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -210,6 +213,14 @@ public class SettingsActivity extends Activity case KEY_MINUS_ONE: return LineageUtils.hasPackageInstalled(getActivity(), LineageLauncherCallbacks.SEARCH_PACKAGE); + + case KEY_TRUST_APPS: + preference.setOnPreferenceClickListener(p -> { + Intent intent = new Intent(getActivity(), TrustAppsActivity.class); + startActivity(intent); + return true; + }); + return true; } return true; diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index 03493a538..918b631bc 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -26,6 +26,7 @@ import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS; import android.app.AlertDialog; import android.content.ActivityNotFoundException; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; @@ -55,6 +56,7 @@ import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.views.FloatingIconView; import com.android.launcher3.widget.PendingAppWidgetHostView; @@ -267,6 +269,15 @@ public class ItemClickHandler { // Preload the icon to reduce latency b/w swapping the floating view with the original. FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */); } - launcher.startActivitySafely(v, intent, item, sourceContainer); + + TrustDatabaseHelper db = TrustDatabaseHelper.getInstance(launcher); + ComponentName cn = item.getTargetComponent(); + boolean isProtected = cn != null && db.isPackageProtected(cn.getPackageName()); + + if (isProtected) { + launcher.startActivitySafelyAuth(v, intent, item, sourceContainer); + } else { + launcher.startActivitySafely(v, intent, item, sourceContainer); + } } } |