2 * Copyright (C) 2015 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.settings.applications;
19 import android.app.Activity;
20 import android.app.ProgressDialog;
21 import android.content.ComponentName;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.ActivityInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.content.res.Configuration;
29 import android.graphics.drawable.Drawable;
30 import android.os.AsyncTask;
31 import android.os.Bundle;
32 import android.provider.Settings;
33 import android.view.LayoutInflater;
34 import android.view.Menu;
35 import android.view.MenuItem;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.widget.ArrayAdapter;
39 import android.widget.CheckBox;
40 import android.widget.ImageView;
41 import android.widget.ListView;
42 import android.widget.TextView;
43 import com.android.settings.R;
44 import com.android.settings.cyanogenmod.ProtectedAppsReceiver;
46 import cyanogenmod.providers.CMSettings;
48 import java.util.ArrayList;
49 import java.util.Collections;
50 import java.util.HashSet;
51 import java.util.List;
52 import java.util.concurrent.ConcurrentHashMap;
54 public class ProtectedAppsActivity extends Activity {
55 private static final int REQ_ENTER_PATTERN = 1;
56 private static final int REQ_RESET_PATTERN = 2;
58 private static final String NEEDS_UNLOCK = "needs_unlock";
59 private static final String TARGET_INTENT = "target_intent";
61 private ListView mListView;
63 private static final int MENU_RESET = 0;
64 private static final int MENU_RESET_LOCK = 1;
66 private PackageManager mPackageManager;
68 private AppsAdapter mAppsAdapter;
70 private ArrayList<ComponentName> mProtect;
72 private boolean mWaitUserAuth = false;
73 private boolean mUserIsAuth = false;
74 private Intent mTargetIntent;
75 private int mOrientation;
77 private HashSet<ComponentName> mProtectedApps = new HashSet<ComponentName>();
80 protected void onCreate(Bundle savedInstanceState) {
81 super.onCreate(savedInstanceState);
83 // Handle incoming target activity
84 Intent incomingIntent = getIntent();
85 if (incomingIntent.hasExtra("com.android.settings.PROTECTED_APP_TARGET_INTENT")) {
87 incomingIntent.getParcelableExtra(
88 "com.android.settings.PROTECTED_APP_TARGET_INTENT");
91 setTitle(R.string.protected_apps);
92 setContentView(R.layout.hidden_apps_list);
94 mPackageManager = getPackageManager();
95 mAppsAdapter = new AppsAdapter(this, R.layout.hidden_apps_list_item);
96 mAppsAdapter.setNotifyOnChange(true);
98 mListView = (ListView) findViewById(R.id.protected_apps_list);
99 mListView.setAdapter(mAppsAdapter);
101 mProtect = new ArrayList<ComponentName>();
103 if (savedInstanceState != null) {
104 mUserIsAuth = savedInstanceState.getBoolean(NEEDS_UNLOCK);
105 mTargetIntent = savedInstanceState.getParcelable(TARGET_INTENT);
109 mWaitUserAuth = true;
110 Intent lockPattern = new Intent(this, LockPatternActivity.class);
111 startActivityForResult(lockPattern, REQ_ENTER_PATTERN);
114 if (mTargetIntent != null) {
115 launchTargetActivityInfoAndFinish();
119 mOrientation = getResources().getConfiguration().orientation;
123 protected void onSaveInstanceState(Bundle outState) {
124 super.onSaveInstanceState(outState);
125 outState.putBoolean(NEEDS_UNLOCK, mUserIsAuth);
126 outState.putParcelable(TARGET_INTENT, mTargetIntent);
130 protected void onResume() {
133 AsyncTask<Void, Void, List<AppEntry>> refreshAppsTask =
134 new AsyncTask<Void, Void, List<AppEntry>>() {
137 protected void onPostExecute(List<AppEntry> apps) {
138 mAppsAdapter.clear();
139 mAppsAdapter.addAll(apps);
143 protected List<AppEntry> doInBackground(Void... params) {
144 return refreshApps();
147 refreshAppsTask.execute(null, null, null);
149 getActionBar().setDisplayHomeAsUpEnabled(true);
151 // Update Protected Apps list
152 updateProtectedComponentsList();
155 private void updateProtectedComponentsList() {
156 String protectedComponents = CMSettings.Secure.getString(getContentResolver(),
157 CMSettings.Secure.PROTECTED_COMPONENTS);
158 protectedComponents = protectedComponents == null ? "" : protectedComponents;
159 String [] flattened = protectedComponents.split("\\|");
160 mProtectedApps = new HashSet<ComponentName>(flattened.length);
161 for (String flat : flattened) {
162 ComponentName cmp = ComponentName.unflattenFromString(flat);
164 mProtectedApps.add(cmp);
170 public void onPause() {
173 // Close this app to prevent unauthorized access when
174 // 1) not waiting for authorization and
175 // 2) there is no portrait/landscape mode switching
176 if (!mWaitUserAuth && (mOrientation == getResources().getConfiguration().orientation)) {
181 private boolean getProtectedStateFromComponentName(ComponentName componentName) {
182 return mProtectedApps.contains(componentName);
186 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
187 switch (requestCode) {
188 case REQ_ENTER_PATTERN:
189 mWaitUserAuth = false;
190 switch (resultCode) {
192 //Nothing to do, proceed!
194 if (mTargetIntent != null) {
195 launchTargetActivityInfoAndFinish();
198 case RESULT_CANCELED:
199 // user failed to define a pattern, do not lock the folder
204 case REQ_RESET_PATTERN:
205 mWaitUserAuth = false;
210 private void launchTargetActivityInfoAndFinish() {
211 Intent launchIntent = mTargetIntent;
212 startActivity(launchIntent);
217 public boolean onCreateOptionsMenu(Menu menu) {
218 menu.add(0, MENU_RESET, 0, R.string.menu_hidden_apps_delete)
219 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
220 menu.add(0, MENU_RESET_LOCK, 0, R.string.menu_hidden_apps_reset_lock)
221 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
225 private void reset() {
226 ArrayList<ComponentName> componentsList = new ArrayList<ComponentName>();
228 // Check to see if any components that have been protected that aren't present in
229 // the ListView. This can happen if there are components which have been protected
230 // but do not respond to the queryIntentActivities for Launcher Category
231 ContentResolver resolver = getContentResolver();
232 String hiddenComponents = CMSettings.Secure.getString(resolver,
233 CMSettings.Secure.PROTECTED_COMPONENTS);
235 if (hiddenComponents != null && !hiddenComponents.equals("")) {
236 for (String flattened : hiddenComponents.split("\\|")) {
237 ComponentName cmp = ComponentName.unflattenFromString(flattened);
239 if (!componentsList.contains(cmp)) {
240 componentsList.add(cmp);
245 AppProtectList list = new AppProtectList(componentsList,
246 PackageManager.COMPONENT_VISIBLE_STATUS);
247 StoreComponentProtectedStatus task = new StoreComponentProtectedStatus(this);
251 private void resetLock() {
252 mWaitUserAuth = true;
253 Intent lockPattern = new Intent(LockPatternActivity.RECREATE_PATTERN, null,
254 this, LockPatternActivity.class);
255 startActivityForResult(lockPattern, REQ_RESET_PATTERN);
258 private List<AppEntry> refreshApps() {
259 Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
260 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
261 List<ResolveInfo> apps = mPackageManager.queryIntentActivities(mainIntent, 0);
262 Collections.sort(apps, new ResolveInfo.DisplayNameComparator(mPackageManager));
263 List<AppEntry> appEntries = new ArrayList<AppEntry>(apps.size());
264 for (ResolveInfo info : apps) {
265 appEntries.add(new AppEntry(info));
271 public boolean onOptionsItemSelected(MenuItem item) {
272 switch (item.getItemId()) {
276 case MENU_RESET_LOCK:
279 case android.R.id.home:
283 return super.onOptionsItemSelected(item);
287 private final class AppEntry {
288 public final ComponentName componentName;
289 public final String title;
291 public AppEntry(ResolveInfo info) {
292 ActivityInfo aInfo = info.activityInfo;
293 componentName = new ComponentName(aInfo.packageName, aInfo.name);
294 title = info.loadLabel(mPackageManager).toString();
298 private final class AppProtectList {
299 public final ArrayList<ComponentName> componentNames;
300 public final boolean state;
302 public AppProtectList(ArrayList<ComponentName> componentNames, boolean state) {
303 this.componentNames = new ArrayList<ComponentName>();
304 for (ComponentName cn : componentNames) {
305 this.componentNames.add(cn.clone());
312 public class StoreComponentProtectedStatus extends AsyncTask<AppProtectList, Void, Void> {
313 private ProgressDialog mDialog;
314 private Context mContext;
316 public StoreComponentProtectedStatus(Context context) {
318 mDialog = new ProgressDialog(mContext);
322 protected void onPreExecute() {
323 mDialog.setMessage(getResources().getString(R.string.saving_protected_components));
324 mDialog.setCancelable(false);
325 mDialog.setCanceledOnTouchOutside(false);
330 protected void onPostExecute(Void aVoid) {
331 if (mDialog.isShowing()) {
335 mAppsAdapter.notifyDataSetChanged();
339 protected Void doInBackground(final AppProtectList... args) {
340 for (AppProtectList appList : args) {
341 ProtectedAppsReceiver.updateProtectedAppComponentsAndNotify(mContext,
342 appList.componentNames, appList.state);
345 updateProtectedComponentsList();
351 * App view holder used to reuse the views inside the list.
353 private static class AppViewHolder {
354 public final View container;
355 public final TextView title;
356 public final ImageView icon;
357 public final View launch;
358 public final CheckBox checkBox;
360 public AppViewHolder(View parentView) {
361 container = parentView.findViewById(R.id.app_item);
362 icon = (ImageView) parentView.findViewById(R.id.icon);
363 title = (TextView) parentView.findViewById(R.id.title);
364 launch = parentView.findViewById(R.id.launch_app);
365 checkBox = (CheckBox) parentView.findViewById(R.id.checkbox);
370 public void onConfigurationChanged(Configuration newConfig) {
371 super.onConfigurationChanged(newConfig);
374 public class AppsAdapter extends ArrayAdapter<AppEntry> {
376 private final LayoutInflater mInflator;
378 private ConcurrentHashMap<String, Drawable> mIcons;
379 private Drawable mDefaultImg;
380 private List<AppEntry> mApps;
382 public AppsAdapter(Context context, int textViewResourceId) {
383 super(context, textViewResourceId);
385 mApps = new ArrayList<AppEntry>();
387 mInflator = LayoutInflater.from(context);
389 // set the default icon till the actual app icon is loaded in async task
390 mDefaultImg = context.getResources().getDrawable(android.R.mipmap.sym_def_app_icon);
391 mIcons = new ConcurrentHashMap<String, Drawable>();
395 public View getView(final int position, View convertView, ViewGroup parent) {
396 AppViewHolder viewHolder;
398 if (convertView == null) {
399 convertView = mInflator.inflate(R.layout.hidden_apps_list_item, parent, false);
400 viewHolder = new AppViewHolder(convertView);
401 convertView.setTag(viewHolder);
403 viewHolder = (AppViewHolder) convertView.getTag();
406 AppEntry app = getItem(position);
408 viewHolder.title.setText(app.title);
410 Drawable icon = mIcons.get(app.componentName.getPackageName());
411 viewHolder.icon.setImageDrawable(icon != null ? icon : mDefaultImg);
413 boolean state = getProtectedStateFromComponentName(app.componentName);
414 viewHolder.checkBox.setChecked(state);
416 viewHolder.launch.setVisibility(View.VISIBLE);
417 viewHolder.launch.setTag(app);
418 viewHolder.launch.setOnClickListener(new View.OnClickListener() {
420 public void onClick(View v) {
421 ComponentName cName = ((AppEntry)v.getTag()).componentName;
422 Intent intent = new Intent();
423 intent.setClassName(cName.getPackageName(), cName.getClassName());
424 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
425 startActivity(intent);
429 viewHolder.launch.setVisibility(View.GONE);
432 viewHolder.container.setTag(position);
433 viewHolder.container.setOnClickListener(mAppClickListener);
438 public boolean hasStableIds() {
443 public void notifyDataSetChanged() {
444 super.notifyDataSetChanged();
445 // If we have new items, we have to load their icons
446 // If items were deleted, remove them from our mApps
447 List<AppEntry> newApps = new ArrayList<AppEntry>(getCount());
448 List<AppEntry> oldApps = new ArrayList<AppEntry>(getCount());
449 for (int i = 0; i < getCount(); i++) {
450 AppEntry app = getItem(i);
451 if (mApps.contains(app)) {
458 if (newApps.size() > 0) {
459 new LoadIconsTask().execute(newApps.toArray(new AppEntry[] {}));
460 newApps.addAll(oldApps);
468 * An asynchronous task to load the icons of the installed applications.
470 private class LoadIconsTask extends AsyncTask<AppEntry, Void, Void> {
472 protected Void doInBackground(AppEntry... apps) {
473 for (AppEntry app : apps) {
475 String packageName = app.componentName.getPackageName();
476 if (mIcons.containsKey(packageName)) {
479 Drawable icon = mPackageManager.getApplicationIcon(packageName);
480 mIcons.put(packageName, icon);
482 } catch (PackageManager.NameNotFoundException e) {
483 // ignored; app will show up with default image
491 protected void onProgressUpdate(Void... progress) {
492 notifyDataSetChanged();
497 private View.OnClickListener mAppClickListener = new View.OnClickListener() {
499 public void onClick(View v) {
500 int position = (Integer) v.getTag();
501 ComponentName cn = mAppsAdapter.getItem(position).componentName;
502 ArrayList<ComponentName> componentsList = new ArrayList<ComponentName>();
503 componentsList.add(cn);
504 boolean state = getProtectedStateFromComponentName(cn);
506 AppProtectList list = new AppProtectList(componentsList, state);
507 StoreComponentProtectedStatus task =
508 new StoreComponentProtectedStatus(ProtectedAppsActivity.this);