diff options
Diffstat (limited to 'src/com/android/settings/applications/ProtectedAppsActivity.java')
-rw-r--r-- | src/com/android/settings/applications/ProtectedAppsActivity.java | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/src/com/android/settings/applications/ProtectedAppsActivity.java b/src/com/android/settings/applications/ProtectedAppsActivity.java new file mode 100644 index 0000000..7156c92 --- /dev/null +++ b/src/com/android/settings/applications/ProtectedAppsActivity.java @@ -0,0 +1,512 @@ +/* + * Copyright (C) 2015 The Android Open Source 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.settings.applications; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.provider.Settings; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import com.android.settings.R; +import com.android.settings.cyanogenmod.ProtectedAppsReceiver; + +import cyanogenmod.providers.CMSettings; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +public class ProtectedAppsActivity extends Activity { + private static final int REQ_ENTER_PATTERN = 1; + private static final int REQ_RESET_PATTERN = 2; + + private static final String NEEDS_UNLOCK = "needs_unlock"; + private static final String TARGET_INTENT = "target_intent"; + + private ListView mListView; + + private static final int MENU_RESET = 0; + private static final int MENU_RESET_LOCK = 1; + + private PackageManager mPackageManager; + + private AppsAdapter mAppsAdapter; + + private ArrayList<ComponentName> mProtect; + + private boolean mWaitUserAuth = false; + private boolean mUserIsAuth = false; + private Intent mTargetIntent; + private int mOrientation; + + private HashSet<ComponentName> mProtectedApps = new HashSet<ComponentName>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Handle incoming target activity + Intent incomingIntent = getIntent(); + if (incomingIntent.hasExtra("com.android.settings.PROTECTED_APP_TARGET_INTENT")) { + mTargetIntent = + incomingIntent.getParcelableExtra( + "com.android.settings.PROTECTED_APP_TARGET_INTENT"); + } + + setTitle(R.string.protected_apps); + setContentView(R.layout.hidden_apps_list); + + mPackageManager = getPackageManager(); + mAppsAdapter = new AppsAdapter(this, R.layout.hidden_apps_list_item); + mAppsAdapter.setNotifyOnChange(true); + + mListView = (ListView) findViewById(R.id.protected_apps_list); + mListView.setAdapter(mAppsAdapter); + + mProtect = new ArrayList<ComponentName>(); + + if (savedInstanceState != null) { + mUserIsAuth = savedInstanceState.getBoolean(NEEDS_UNLOCK); + mTargetIntent = savedInstanceState.getParcelable(TARGET_INTENT); + } else { + if (!mUserIsAuth) { + // Require unlock + mWaitUserAuth = true; + Intent lockPattern = new Intent(this, LockPatternActivity.class); + startActivityForResult(lockPattern, REQ_ENTER_PATTERN); + } else { + //LAUNCH + if (mTargetIntent != null) { + launchTargetActivityInfoAndFinish(); + } + } + } + mOrientation = getResources().getConfiguration().orientation; + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(NEEDS_UNLOCK, mUserIsAuth); + outState.putParcelable(TARGET_INTENT, mTargetIntent); + } + + @Override + protected void onResume() { + super.onResume(); + + AsyncTask<Void, Void, List<AppEntry>> refreshAppsTask = + new AsyncTask<Void, Void, List<AppEntry>>() { + + @Override + protected void onPostExecute(List<AppEntry> apps) { + mAppsAdapter.clear(); + mAppsAdapter.addAll(apps); + } + + @Override + protected List<AppEntry> doInBackground(Void... params) { + return refreshApps(); + } + }; + refreshAppsTask.execute(null, null, null); + + getActionBar().setDisplayHomeAsUpEnabled(true); + + // Update Protected Apps list + updateProtectedComponentsList(); + } + + private void updateProtectedComponentsList() { + String protectedComponents = CMSettings.Secure.getString(getContentResolver(), + CMSettings.Secure.PROTECTED_COMPONENTS); + protectedComponents = protectedComponents == null ? "" : protectedComponents; + String [] flattened = protectedComponents.split("\\|"); + mProtectedApps = new HashSet<ComponentName>(flattened.length); + for (String flat : flattened) { + ComponentName cmp = ComponentName.unflattenFromString(flat); + if (cmp != null) { + mProtectedApps.add(cmp); + } + } + } + + @Override + public void onPause() { + super.onPause(); + + // Close this app to prevent unauthorized access when + // 1) not waiting for authorization and + // 2) there is no portrait/landscape mode switching + if (!mWaitUserAuth && (mOrientation == getResources().getConfiguration().orientation)) { + finish(); + } + } + + private boolean getProtectedStateFromComponentName(ComponentName componentName) { + return mProtectedApps.contains(componentName); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQ_ENTER_PATTERN: + mWaitUserAuth = false; + switch (resultCode) { + case RESULT_OK: + //Nothing to do, proceed! + mUserIsAuth = true; + if (mTargetIntent != null) { + launchTargetActivityInfoAndFinish(); + } + break; + case RESULT_CANCELED: + // user failed to define a pattern, do not lock the folder + finish(); + break; + } + break; + case REQ_RESET_PATTERN: + mWaitUserAuth = false; + mUserIsAuth = false; + } + } + + private void launchTargetActivityInfoAndFinish() { + Intent launchIntent = mTargetIntent; + startActivity(launchIntent); + finish(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, MENU_RESET, 0, R.string.menu_hidden_apps_delete) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + menu.add(0, MENU_RESET_LOCK, 0, R.string.menu_hidden_apps_reset_lock) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + return true; + } + + private void reset() { + ArrayList<ComponentName> componentsList = new ArrayList<ComponentName>(); + + // Check to see if any components that have been protected that aren't present in + // the ListView. This can happen if there are components which have been protected + // but do not respond to the queryIntentActivities for Launcher Category + ContentResolver resolver = getContentResolver(); + String hiddenComponents = CMSettings.Secure.getString(resolver, + CMSettings.Secure.PROTECTED_COMPONENTS); + + if (hiddenComponents != null && !hiddenComponents.equals("")) { + for (String flattened : hiddenComponents.split("\\|")) { + ComponentName cmp = ComponentName.unflattenFromString(flattened); + + if (!componentsList.contains(cmp)) { + componentsList.add(cmp); + } + } + } + + AppProtectList list = new AppProtectList(componentsList, + PackageManager.COMPONENT_VISIBLE_STATUS); + StoreComponentProtectedStatus task = new StoreComponentProtectedStatus(this); + task.execute(list); + } + + private void resetLock() { + mWaitUserAuth = true; + Intent lockPattern = new Intent(LockPatternActivity.RECREATE_PATTERN, null, + this, LockPatternActivity.class); + startActivityForResult(lockPattern, REQ_RESET_PATTERN); + } + + private List<AppEntry> refreshApps() { + Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); + List<ResolveInfo> apps = mPackageManager.queryIntentActivities(mainIntent, 0); + Collections.sort(apps, new ResolveInfo.DisplayNameComparator(mPackageManager)); + List<AppEntry> appEntries = new ArrayList<AppEntry>(apps.size()); + for (ResolveInfo info : apps) { + appEntries.add(new AppEntry(info)); + } + return appEntries; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_RESET: + reset(); + return true; + case MENU_RESET_LOCK: + resetLock(); + return true; + case android.R.id.home: + finish(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private final class AppEntry { + public final ComponentName componentName; + public final String title; + + public AppEntry(ResolveInfo info) { + ActivityInfo aInfo = info.activityInfo; + componentName = new ComponentName(aInfo.packageName, aInfo.name); + title = info.loadLabel(mPackageManager).toString(); + } + } + + private final class AppProtectList { + public final ArrayList<ComponentName> componentNames; + public final boolean state; + + public AppProtectList(ArrayList<ComponentName> componentNames, boolean state) { + this.componentNames = new ArrayList<ComponentName>(); + for (ComponentName cn : componentNames) { + this.componentNames.add(cn.clone()); + } + + this.state = state; + } + } + + public class StoreComponentProtectedStatus extends AsyncTask<AppProtectList, Void, Void> { + private ProgressDialog mDialog; + private Context mContext; + + public StoreComponentProtectedStatus(Context context) { + mContext = context; + mDialog = new ProgressDialog(mContext); + } + + @Override + protected void onPreExecute() { + mDialog.setMessage(getResources().getString(R.string.saving_protected_components)); + mDialog.setCancelable(false); + mDialog.setCanceledOnTouchOutside(false); + mDialog.show(); + } + + @Override + protected void onPostExecute(Void aVoid) { + if (mDialog.isShowing()) { + mDialog.dismiss(); + } + + mAppsAdapter.notifyDataSetChanged(); + } + + @Override + protected Void doInBackground(final AppProtectList... args) { + for (AppProtectList appList : args) { + ProtectedAppsReceiver.updateProtectedAppComponentsAndNotify(mContext, + appList.componentNames, appList.state); + } + + updateProtectedComponentsList(); + return null; + } + } + + /** + * App view holder used to reuse the views inside the list. + */ + private static class AppViewHolder { + public final View container; + public final TextView title; + public final ImageView icon; + public final View launch; + public final CheckBox checkBox; + + public AppViewHolder(View parentView) { + container = parentView.findViewById(R.id.app_item); + icon = (ImageView) parentView.findViewById(R.id.icon); + title = (TextView) parentView.findViewById(R.id.title); + launch = parentView.findViewById(R.id.launch_app); + checkBox = (CheckBox) parentView.findViewById(R.id.checkbox); + } + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + } + + public class AppsAdapter extends ArrayAdapter<AppEntry> { + + private final LayoutInflater mInflator; + + private ConcurrentHashMap<String, Drawable> mIcons; + private Drawable mDefaultImg; + private List<AppEntry> mApps; + + public AppsAdapter(Context context, int textViewResourceId) { + super(context, textViewResourceId); + + mApps = new ArrayList<AppEntry>(); + + mInflator = LayoutInflater.from(context); + + // set the default icon till the actual app icon is loaded in async task + mDefaultImg = context.getResources().getDrawable(android.R.mipmap.sym_def_app_icon); + mIcons = new ConcurrentHashMap<String, Drawable>(); + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + AppViewHolder viewHolder; + + if (convertView == null) { + convertView = mInflator.inflate(R.layout.hidden_apps_list_item, parent, false); + viewHolder = new AppViewHolder(convertView); + convertView.setTag(viewHolder); + } else { + viewHolder = (AppViewHolder) convertView.getTag(); + } + + AppEntry app = getItem(position); + + viewHolder.title.setText(app.title); + + Drawable icon = mIcons.get(app.componentName.getPackageName()); + viewHolder.icon.setImageDrawable(icon != null ? icon : mDefaultImg); + + boolean state = getProtectedStateFromComponentName(app.componentName); + viewHolder.checkBox.setChecked(state); + if (state) { + viewHolder.launch.setVisibility(View.VISIBLE); + viewHolder.launch.setTag(app); + viewHolder.launch.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ComponentName cName = ((AppEntry)v.getTag()).componentName; + Intent intent = new Intent(); + intent.setClassName(cName.getPackageName(), cName.getClassName()); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + }); + } else { + viewHolder.launch.setVisibility(View.GONE); + } + + viewHolder.container.setTag(position); + viewHolder.container.setOnClickListener(mAppClickListener); + return convertView; + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + // If we have new items, we have to load their icons + // If items were deleted, remove them from our mApps + List<AppEntry> newApps = new ArrayList<AppEntry>(getCount()); + List<AppEntry> oldApps = new ArrayList<AppEntry>(getCount()); + for (int i = 0; i < getCount(); i++) { + AppEntry app = getItem(i); + if (mApps.contains(app)) { + oldApps.add(app); + } else { + newApps.add(app); + } + } + + if (newApps.size() > 0) { + new LoadIconsTask().execute(newApps.toArray(new AppEntry[] {})); + newApps.addAll(oldApps); + mApps = newApps; + } else { + mApps = oldApps; + } + } + + /** + * An asynchronous task to load the icons of the installed applications. + */ + private class LoadIconsTask extends AsyncTask<AppEntry, Void, Void> { + @Override + protected Void doInBackground(AppEntry... apps) { + for (AppEntry app : apps) { + try { + String packageName = app.componentName.getPackageName(); + if (mIcons.containsKey(packageName)) { + continue; + } + Drawable icon = mPackageManager.getApplicationIcon(packageName); + mIcons.put(packageName, icon); + publishProgress(); + } catch (PackageManager.NameNotFoundException e) { + // ignored; app will show up with default image + } + } + + return null; + } + + @Override + protected void onProgressUpdate(Void... progress) { + notifyDataSetChanged(); + } + } + } + + private View.OnClickListener mAppClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + int position = (Integer) v.getTag(); + ComponentName cn = mAppsAdapter.getItem(position).componentName; + ArrayList<ComponentName> componentsList = new ArrayList<ComponentName>(); + componentsList.add(cn); + boolean state = getProtectedStateFromComponentName(cn); + + AppProtectList list = new AppProtectList(componentsList, state); + StoreComponentProtectedStatus task = + new StoreComponentProtectedStatus(ProtectedAppsActivity.this); + task.execute(list); + } + }; +} |