OSDN Git Service

Merge tag 'android-6.0.1_r74' into HEAD
[android-x86/packages-apps-Settings.git] / src / com / android / settings / applications / ProtectedAppsActivity.java
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.settings.applications;
18
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;
45
46 import cyanogenmod.providers.CMSettings;
47
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;
53
54 public class ProtectedAppsActivity extends Activity {
55     private static final int REQ_ENTER_PATTERN = 1;
56     private static final int REQ_RESET_PATTERN = 2;
57
58     private static final String NEEDS_UNLOCK = "needs_unlock";
59     private static final String TARGET_INTENT = "target_intent";
60
61     private ListView mListView;
62
63     private static final int MENU_RESET = 0;
64     private static final int MENU_RESET_LOCK = 1;
65
66     private PackageManager mPackageManager;
67
68     private AppsAdapter mAppsAdapter;
69
70     private ArrayList<ComponentName> mProtect;
71
72     private boolean mWaitUserAuth = false;
73     private boolean mUserIsAuth = false;
74     private Intent mTargetIntent;
75     private int mOrientation;
76
77     private HashSet<ComponentName> mProtectedApps = new HashSet<ComponentName>();
78
79     @Override
80     protected void onCreate(Bundle savedInstanceState) {
81         super.onCreate(savedInstanceState);
82
83         // Handle incoming target activity
84         Intent incomingIntent = getIntent();
85         if (incomingIntent.hasExtra("com.android.settings.PROTECTED_APP_TARGET_INTENT")) {
86             mTargetIntent =
87                     incomingIntent.getParcelableExtra(
88                             "com.android.settings.PROTECTED_APP_TARGET_INTENT");
89         }
90
91         setTitle(R.string.protected_apps);
92         setContentView(R.layout.hidden_apps_list);
93
94         mPackageManager = getPackageManager();
95         mAppsAdapter = new AppsAdapter(this, R.layout.hidden_apps_list_item);
96         mAppsAdapter.setNotifyOnChange(true);
97
98         mListView = (ListView) findViewById(R.id.protected_apps_list);
99         mListView.setAdapter(mAppsAdapter);
100
101         mProtect = new ArrayList<ComponentName>();
102
103         if (savedInstanceState != null) {
104             mUserIsAuth = savedInstanceState.getBoolean(NEEDS_UNLOCK);
105             mTargetIntent = savedInstanceState.getParcelable(TARGET_INTENT);
106         } else {
107             if (!mUserIsAuth) {
108                 // Require unlock
109                 mWaitUserAuth = true;
110                 Intent lockPattern = new Intent(this, LockPatternActivity.class);
111                 startActivityForResult(lockPattern, REQ_ENTER_PATTERN);
112             } else {
113                 //LAUNCH
114                 if (mTargetIntent != null) {
115                     launchTargetActivityInfoAndFinish();
116                 }
117             }
118         }
119         mOrientation = getResources().getConfiguration().orientation;
120     }
121
122     @Override
123     protected void onSaveInstanceState(Bundle outState) {
124         super.onSaveInstanceState(outState);
125         outState.putBoolean(NEEDS_UNLOCK, mUserIsAuth);
126         outState.putParcelable(TARGET_INTENT, mTargetIntent);
127     }
128
129     @Override
130     protected void onResume() {
131         super.onResume();
132
133         AsyncTask<Void, Void, List<AppEntry>> refreshAppsTask =
134                 new AsyncTask<Void, Void, List<AppEntry>>() {
135
136                     @Override
137                     protected void onPostExecute(List<AppEntry> apps) {
138                         mAppsAdapter.clear();
139                         mAppsAdapter.addAll(apps);
140                     }
141
142                     @Override
143                     protected List<AppEntry> doInBackground(Void... params) {
144                         return refreshApps();
145                     }
146                 };
147         refreshAppsTask.execute(null, null, null);
148
149         getActionBar().setDisplayHomeAsUpEnabled(true);
150
151         // Update Protected Apps list
152         updateProtectedComponentsList();
153     }
154
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);
163             if (cmp != null) {
164                 mProtectedApps.add(cmp);
165             }
166         }
167     }
168
169     @Override
170     public void onPause() {
171         super.onPause();
172
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)) {
177             finish();
178         }
179     }
180
181     private boolean getProtectedStateFromComponentName(ComponentName componentName) {
182         return mProtectedApps.contains(componentName);
183     }
184
185     @Override
186     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
187         switch (requestCode) {
188             case REQ_ENTER_PATTERN:
189                 mWaitUserAuth = false;
190                 switch (resultCode) {
191                     case RESULT_OK:
192                         //Nothing to do, proceed!
193                         mUserIsAuth = true;
194                         if (mTargetIntent != null) {
195                             launchTargetActivityInfoAndFinish();
196                         }
197                         break;
198                     case RESULT_CANCELED:
199                         // user failed to define a pattern, do not lock the folder
200                         finish();
201                         break;
202                 }
203                 break;
204             case REQ_RESET_PATTERN:
205                 mWaitUserAuth = false;
206                 mUserIsAuth = false;
207         }
208     }
209
210     private void launchTargetActivityInfoAndFinish() {
211         Intent launchIntent = mTargetIntent;
212         startActivity(launchIntent);
213         finish();
214     }
215
216     @Override
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);
222         return true;
223     }
224
225     private void reset() {
226         ArrayList<ComponentName> componentsList = new ArrayList<ComponentName>();
227
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);
234
235         if (hiddenComponents != null && !hiddenComponents.equals("")) {
236             for (String flattened : hiddenComponents.split("\\|")) {
237                 ComponentName cmp = ComponentName.unflattenFromString(flattened);
238
239                 if (!componentsList.contains(cmp)) {
240                     componentsList.add(cmp);
241                 }
242             }
243         }
244
245         AppProtectList list = new AppProtectList(componentsList,
246                 PackageManager.COMPONENT_VISIBLE_STATUS);
247         StoreComponentProtectedStatus task = new StoreComponentProtectedStatus(this);
248         task.execute(list);
249     }
250
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);
256     }
257
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));
266         }
267         return appEntries;
268     }
269
270     @Override
271     public boolean onOptionsItemSelected(MenuItem item) {
272         switch (item.getItemId()) {
273             case MENU_RESET:
274                 reset();
275                 return true;
276             case MENU_RESET_LOCK:
277                 resetLock();
278                 return true;
279             case android.R.id.home:
280                 finish();
281                 return true;
282             default:
283                 return super.onOptionsItemSelected(item);
284         }
285     }
286
287     private final class AppEntry {
288         public final ComponentName componentName;
289         public final String title;
290
291         public AppEntry(ResolveInfo info) {
292             ActivityInfo aInfo = info.activityInfo;
293             componentName = new ComponentName(aInfo.packageName, aInfo.name);
294             title = info.loadLabel(mPackageManager).toString();
295         }
296     }
297
298     private final class AppProtectList {
299         public final ArrayList<ComponentName> componentNames;
300         public final boolean state;
301
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());
306             }
307
308             this.state = state;
309         }
310     }
311
312     public class StoreComponentProtectedStatus extends AsyncTask<AppProtectList, Void, Void> {
313         private ProgressDialog mDialog;
314         private Context mContext;
315
316         public StoreComponentProtectedStatus(Context context) {
317             mContext = context;
318             mDialog = new ProgressDialog(mContext);
319         }
320
321         @Override
322         protected void onPreExecute() {
323             mDialog.setMessage(getResources().getString(R.string.saving_protected_components));
324             mDialog.setCancelable(false);
325             mDialog.setCanceledOnTouchOutside(false);
326             mDialog.show();
327         }
328
329         @Override
330         protected void onPostExecute(Void aVoid) {
331             if (mDialog.isShowing()) {
332                 mDialog.dismiss();
333             }
334
335             mAppsAdapter.notifyDataSetChanged();
336         }
337
338         @Override
339         protected Void doInBackground(final AppProtectList... args) {
340             for (AppProtectList appList : args) {
341                 ProtectedAppsReceiver.updateProtectedAppComponentsAndNotify(mContext,
342                         appList.componentNames, appList.state);
343             }
344
345             updateProtectedComponentsList();
346             return null;
347         }
348     }
349
350     /**
351      * App view holder used to reuse the views inside the list.
352      */
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;
359
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);
366         }
367     }
368
369     @Override
370     public void onConfigurationChanged(Configuration newConfig) {
371         super.onConfigurationChanged(newConfig);
372     }
373
374     public class AppsAdapter extends ArrayAdapter<AppEntry> {
375
376         private final LayoutInflater mInflator;
377
378         private ConcurrentHashMap<String, Drawable> mIcons;
379         private Drawable mDefaultImg;
380         private List<AppEntry> mApps;
381
382         public AppsAdapter(Context context, int textViewResourceId) {
383             super(context, textViewResourceId);
384
385             mApps = new ArrayList<AppEntry>();
386
387             mInflator = LayoutInflater.from(context);
388
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>();
392         }
393
394         @Override
395         public View getView(final int position, View convertView, ViewGroup parent) {
396             AppViewHolder viewHolder;
397
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);
402             } else {
403                 viewHolder = (AppViewHolder) convertView.getTag();
404             }
405
406             AppEntry app = getItem(position);
407
408             viewHolder.title.setText(app.title);
409
410             Drawable icon = mIcons.get(app.componentName.getPackageName());
411             viewHolder.icon.setImageDrawable(icon != null ? icon : mDefaultImg);
412
413             boolean state = getProtectedStateFromComponentName(app.componentName);
414             viewHolder.checkBox.setChecked(state);
415             if (state) {
416                 viewHolder.launch.setVisibility(View.VISIBLE);
417                 viewHolder.launch.setTag(app);
418                 viewHolder.launch.setOnClickListener(new View.OnClickListener() {
419                     @Override
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);
426                     }
427                 });
428             } else {
429                 viewHolder.launch.setVisibility(View.GONE);
430             }
431
432             viewHolder.container.setTag(position);
433             viewHolder.container.setOnClickListener(mAppClickListener);
434             return convertView;
435         }
436
437         @Override
438         public boolean hasStableIds() {
439             return true;
440         }
441
442         @Override
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)) {
452                     oldApps.add(app);
453                 } else {
454                     newApps.add(app);
455                 }
456             }
457
458             if (newApps.size() > 0) {
459                 new LoadIconsTask().execute(newApps.toArray(new AppEntry[] {}));
460                 newApps.addAll(oldApps);
461                 mApps = newApps;
462             } else {
463                 mApps = oldApps;
464             }
465         }
466
467         /**
468          * An asynchronous task to load the icons of the installed applications.
469          */
470         private class LoadIconsTask extends AsyncTask<AppEntry, Void, Void> {
471             @Override
472             protected Void doInBackground(AppEntry... apps) {
473                 for (AppEntry app : apps) {
474                     try {
475                         String packageName = app.componentName.getPackageName();
476                         if (mIcons.containsKey(packageName)) {
477                             continue;
478                         }
479                         Drawable icon = mPackageManager.getApplicationIcon(packageName);
480                         mIcons.put(packageName, icon);
481                         publishProgress();
482                     } catch (PackageManager.NameNotFoundException e) {
483                         // ignored; app will show up with default image
484                     }
485                 }
486
487                 return null;
488             }
489
490             @Override
491             protected void onProgressUpdate(Void... progress) {
492                 notifyDataSetChanged();
493             }
494         }
495     }
496
497     private View.OnClickListener mAppClickListener = new View.OnClickListener() {
498         @Override
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);
505
506             AppProtectList list = new AppProtectList(componentsList, state);
507             StoreComponentProtectedStatus task =
508                     new StoreComponentProtectedStatus(ProtectedAppsActivity.this);
509             task.execute(list);
510         }
511     };
512 }