OSDN Git Service

Clear only keystore credential entires
[android-x86/packages-apps-Settings.git] / src / com / android / settings / applications / ManageApplications.java
1 /*
2  * Copyright (C) 2006 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.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ApplicationInfo;
23 import android.content.pm.IntentFilterVerificationInfo;
24 import android.content.pm.PackageManager;
25 import android.os.Bundle;
26 import android.os.Environment;
27 import android.os.UserHandle;
28 import android.os.UserManager;
29 import android.preference.PreferenceFrameLayout;
30 import android.provider.Settings;
31 import android.util.ArraySet;
32 import android.util.Log;
33 import android.view.LayoutInflater;
34 import android.view.Menu;
35 import android.view.MenuInflater;
36 import android.view.MenuItem;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.view.animation.AnimationUtils;
40 import android.widget.AbsListView;
41 import android.widget.AdapterView;
42 import android.widget.AdapterView.OnItemClickListener;
43 import android.widget.AdapterView.OnItemSelectedListener;
44 import android.widget.ArrayAdapter;
45 import android.widget.BaseAdapter;
46 import android.widget.Filter;
47 import android.widget.Filterable;
48 import android.widget.FrameLayout;
49 import android.widget.ListView;
50 import android.widget.Spinner;
51
52 import com.android.internal.logging.MetricsLogger;
53 import com.android.settings.AppHeader;
54 import com.android.settings.HelpUtils;
55 import com.android.settings.InstrumentedFragment;
56 import com.android.settings.R;
57 import com.android.settings.Settings.AllApplicationsActivity;
58 import com.android.settings.Settings.DomainsURLsAppListActivity;
59 import com.android.settings.Settings.HighPowerApplicationsActivity;
60 import com.android.settings.Settings.NotificationAppListActivity;
61 import com.android.settings.Settings.StorageUseActivity;
62 import com.android.settings.Settings.UsageAccessSettingsActivity;
63 import com.android.settings.SettingsActivity;
64 import com.android.settings.Utils;
65 import com.android.settings.applications.AppStateUsageBridge.UsageState;
66 import com.android.settings.applications.ApplicationsState.AppEntry;
67 import com.android.settings.applications.ApplicationsState.AppFilter;
68 import com.android.settings.applications.ApplicationsState.CompoundFilter;
69 import com.android.settings.applications.ApplicationsState.VolumeFilter;
70 import com.android.settings.fuelgauge.HighPowerDetail;
71 import com.android.settings.notification.AppNotificationSettings;
72 import com.android.settings.notification.NotificationBackend;
73 import com.android.settings.notification.NotificationBackend.AppRow;
74
75 import java.util.ArrayList;
76 import java.util.Collections;
77 import java.util.Comparator;
78 import java.util.List;
79
80 /**
81  * Activity to pick an application that will be used to display installation information and
82  * options to uninstall/delete user data for system applications. This activity
83  * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
84  * intent.
85  */
86 public class ManageApplications extends InstrumentedFragment
87         implements OnItemClickListener, OnItemSelectedListener {
88
89     static final String TAG = "ManageApplications";
90     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
91
92     // Intent extras.
93     public static final String EXTRA_CLASSNAME = "classname";
94     // Used for storage only.
95     public static final String EXTRA_VOLUME_UUID = "volumeUuid";
96     public static final String EXTRA_VOLUME_NAME = "volumeName";
97
98     private static final String EXTRA_SORT_ORDER = "sortOrder";
99
100     // attributes used as keys when passing values to InstalledAppDetails activity
101     public static final String APP_CHG = "chg";
102
103     // constant value that can be used to check return code from sub activity.
104     private static final int INSTALLED_APP_DETAILS = 1;
105     private static final int ADVANCED_SETTINGS = 2;
106
107     public static final int SIZE_TOTAL = 0;
108     public static final int SIZE_INTERNAL = 1;
109     public static final int SIZE_EXTERNAL = 2;
110
111     // Filter options used for displayed list of applications
112     // The order which they appear is the order they will show when spinner is present.
113     public static final int FILTER_APPS_POWER_WHITELIST         = 0;
114     public static final int FILTER_APPS_ALL                     = 1;
115     public static final int FILTER_APPS_ENABLED                 = 2;
116     public static final int FILTER_APPS_DISABLED                = 3;
117     public static final int FILTER_APPS_BLOCKED                 = 4;
118     public static final int FILTER_APPS_PRIORITY                = 5;
119     public static final int FILTER_APPS_NO_PEEKING              = 6;
120     public static final int FILTER_APPS_SENSITIVE               = 7;
121     public static final int FILTER_APPS_PERSONAL                = 8;
122     public static final int FILTER_APPS_WORK                    = 9;
123     public static final int FILTER_APPS_WITH_DOMAIN_URLS        = 10;
124     public static final int FILTER_APPS_USAGE_ACCESS            = 11;
125
126     // This is the string labels for the filter modes above, the order must be kept in sync.
127     public static final int[] FILTER_LABELS = new int[] {
128         R.string.high_power_filter_on,        // High power whitelist, on
129         R.string.filter_all_apps,      // All apps
130         R.string.filter_enabled_apps,  // Enabled
131         R.string.filter_apps_disabled, // Disabled
132         R.string.filter_notif_blocked_apps,   // Blocked Notifications
133         R.string.filter_notif_priority_apps,  // Priority Notifications
134         R.string.filter_notif_no_peeking,     // No peeking Notifications
135         R.string.filter_notif_sensitive_apps, // Sensitive Notifications
136         R.string.filter_personal_apps, // Personal
137         R.string.filter_work_apps,     // Work
138         R.string.filter_with_domain_urls_apps,     // Domain URLs
139         R.string.filter_all_apps,      // Usage access screen, never displayed
140     };
141     // This is the actual mapping to filters from FILTER_ constants above, the order must
142     // be kept in sync.
143     public static final AppFilter[] FILTERS = new AppFilter[] {
144         AppStatePowerBridge.FILTER_POWER_WHITELISTED,     // High power whitelist, on
145         ApplicationsState.FILTER_EVERYTHING,  // All apps
146         ApplicationsState.FILTER_ALL_ENABLED, // Enabled
147         ApplicationsState.FILTER_DISABLED,    // Disabled
148         AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED,   // Blocked Notifications
149         AppStateNotificationBridge.FILTER_APP_NOTIFICATION_PRIORITY,  // Priority Notifications
150         AppStateNotificationBridge.FILTER_APP_NOTIFICATION_NO_PEEK,   // No peeking Notifications
151         AppStateNotificationBridge.FILTER_APP_NOTIFICATION_SENSITIVE, // Sensitive Notifications
152         ApplicationsState.FILTER_PERSONAL,    // Personal
153         ApplicationsState.FILTER_WORK,        // Work
154         ApplicationsState.FILTER_WITH_DOMAIN_URLS,   // Apps with Domain URLs
155         AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
156     };
157
158     // sort order
159     private int mSortOrder = R.id.sort_order_alpha;
160
161     // whether showing system apps.
162     private boolean mShowSystem;
163
164     private ApplicationsState mApplicationsState;
165
166     public int mListType;
167     public int mFilter;
168
169     public ApplicationsAdapter mApplications;
170
171     private View mLoadingContainer;
172
173     private View mListContainer;
174
175     // ListView used to display list
176     private ListView mListView;
177
178     // Size resource used for packages whose size computation failed for some reason
179     CharSequence mInvalidSizeStr;
180
181     // layout inflater object used to inflate views
182     private LayoutInflater mInflater;
183
184     private String mCurrentPkgName;
185     private int mCurrentUid;
186
187     private Menu mOptionsMenu;
188
189     public static final int LIST_TYPE_MAIN         = 0;
190     public static final int LIST_TYPE_NOTIFICATION = 1;
191     public static final int LIST_TYPE_DOMAINS_URLS = 2;
192     public static final int LIST_TYPE_STORAGE      = 3;
193     public static final int LIST_TYPE_USAGE_ACCESS = 4;
194     public static final int LIST_TYPE_HIGH_POWER   = 5;
195
196     private View mRootView;
197
198     private View mSpinnerHeader;
199     private Spinner mFilterSpinner;
200     private FilterSpinnerAdapter mFilterAdapter;
201     private NotificationBackend mNotifBackend;
202     private ResetAppsHelper mResetAppsHelper;
203     private String mVolumeUuid;
204     private String mVolumeName;
205
206     @Override
207     public void onCreate(Bundle savedInstanceState) {
208         super.onCreate(savedInstanceState);
209         setHasOptionsMenu(true);
210         mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
211
212         Intent intent = getActivity().getIntent();
213         Bundle args = getArguments();
214         String className = args != null ? args.getString(EXTRA_CLASSNAME) : null;
215         if (className == null) {
216             className = intent.getComponent().getClassName();
217         }
218         if (className.equals(AllApplicationsActivity.class.getName())) {
219             mShowSystem = true;
220         } else if (className.equals(NotificationAppListActivity.class.getName())) {
221             mListType = LIST_TYPE_NOTIFICATION;
222             mNotifBackend = new NotificationBackend();
223         } else if (className.equals(DomainsURLsAppListActivity.class.getName())) {
224             mListType = LIST_TYPE_DOMAINS_URLS;
225         } else if (className.equals(StorageUseActivity.class.getName())) {
226             if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) {
227                 mVolumeUuid = args.getString(EXTRA_VOLUME_UUID);
228                 mVolumeName = args.getString(EXTRA_VOLUME_NAME);
229                 mListType = LIST_TYPE_STORAGE;
230             } else {
231                 // No volume selected, display a normal list, sorted by size.
232                 mListType = LIST_TYPE_MAIN;
233             }
234             mSortOrder = R.id.sort_order_size;
235         } else if (className.equals(UsageAccessSettingsActivity.class.getName())) {
236             mListType = LIST_TYPE_USAGE_ACCESS;
237             getActivity().getActionBar().setTitle(R.string.usage_access_title);
238         } else if (className.equals(HighPowerApplicationsActivity.class.getName())) {
239             mListType = LIST_TYPE_HIGH_POWER;
240             // Default to showing system.
241             mShowSystem = true;
242         } else {
243             mListType = LIST_TYPE_MAIN;
244         }
245         mFilter = getDefaultFilter();
246
247         if (savedInstanceState != null) {
248             mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
249         }
250
251         mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
252
253         mResetAppsHelper = new ResetAppsHelper(getActivity());
254     }
255
256
257     @Override
258     public View onCreateView(LayoutInflater inflater, ViewGroup container,
259             Bundle savedInstanceState) {
260         // initialize the inflater
261         mInflater = inflater;
262
263         mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
264         mLoadingContainer = mRootView.findViewById(R.id.loading_container);
265         mLoadingContainer.setVisibility(View.VISIBLE);
266         mListContainer = mRootView.findViewById(R.id.list_container);
267         if (mListContainer != null) {
268             // Create adapter and list view here
269             View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
270             ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
271             if (emptyView != null) {
272                 lv.setEmptyView(emptyView);
273             }
274             lv.setOnItemClickListener(this);
275             lv.setSaveEnabled(true);
276             lv.setItemsCanFocus(true);
277             lv.setTextFilterEnabled(true);
278             mListView = lv;
279             mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
280             mListView.setAdapter(mApplications);
281             mListView.setRecyclerListener(mApplications);
282
283             Utils.prepareCustomPreferencesList(container, mRootView, mListView, false);
284         }
285
286         // We have to do this now because PreferenceFrameLayout looks at it
287         // only when the view is added.
288         if (container instanceof PreferenceFrameLayout) {
289             ((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true;
290         }
291
292         createHeader();
293
294         mResetAppsHelper.onRestoreInstanceState(savedInstanceState);
295
296         return mRootView;
297     }
298
299     private void createHeader() {
300         Activity activity = getActivity();
301         FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
302         mSpinnerHeader = (ViewGroup) activity.getLayoutInflater()
303                 .inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
304         mFilterSpinner = (Spinner) mSpinnerHeader.findViewById(R.id.filter_spinner);
305         mFilterAdapter = new FilterSpinnerAdapter(this);
306         mFilterSpinner.setAdapter(mFilterAdapter);
307         mFilterSpinner.setOnItemSelectedListener(this);
308         pinnedHeader.addView(mSpinnerHeader, 0);
309
310         mFilterAdapter.enableFilter(getDefaultFilter());
311         if (mListType == LIST_TYPE_MAIN || mListType == LIST_TYPE_NOTIFICATION) {
312             if (UserManager.get(getActivity()).getUserProfiles().size() > 1) {
313                 mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL);
314                 mFilterAdapter.enableFilter(FILTER_APPS_WORK);
315             }
316         }
317         if (mListType == LIST_TYPE_NOTIFICATION) {
318             mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED);
319             mFilterAdapter.enableFilter(FILTER_APPS_PRIORITY);
320             mFilterAdapter.enableFilter(FILTER_APPS_SENSITIVE);
321             mFilterAdapter.enableFilter(FILTER_APPS_NO_PEEKING);
322         }
323         if (mListType == LIST_TYPE_HIGH_POWER) {
324             mFilterAdapter.enableFilter(FILTER_APPS_ALL);
325         }
326         if (mListType == LIST_TYPE_STORAGE) {
327             mApplications.setOverrideFilter(new VolumeFilter(mVolumeUuid));
328         }
329     }
330
331     @Override
332     public void onViewCreated(View view, Bundle savedInstanceState) {
333         super.onViewCreated(view, savedInstanceState);
334         if (mListType == LIST_TYPE_STORAGE) {
335             FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
336             AppHeader.createAppHeader(getActivity(), null, mVolumeName, null, pinnedHeader);
337         }
338     }
339
340     private int getDefaultFilter() {
341         switch (mListType) {
342             case LIST_TYPE_DOMAINS_URLS:
343                 return FILTER_APPS_WITH_DOMAIN_URLS;
344             case LIST_TYPE_USAGE_ACCESS:
345                 return FILTER_APPS_USAGE_ACCESS;
346             case LIST_TYPE_HIGH_POWER:
347                 return FILTER_APPS_POWER_WHITELIST;
348             default:
349                 return FILTER_APPS_ALL;
350         }
351     }
352
353     @Override
354     protected int getMetricsCategory() {
355         switch (mListType) {
356             case LIST_TYPE_MAIN:
357                 return MetricsLogger.MANAGE_APPLICATIONS;
358             case LIST_TYPE_NOTIFICATION:
359                 return MetricsLogger.MANAGE_APPLICATIONS_NOTIFICATIONS;
360             case LIST_TYPE_DOMAINS_URLS:
361                 return MetricsLogger.MANAGE_DOMAIN_URLS;
362             case LIST_TYPE_STORAGE:
363                 return MetricsLogger.APPLICATIONS_STORAGE_APPS;
364             case LIST_TYPE_USAGE_ACCESS:
365                 return MetricsLogger.USAGE_ACCESS;
366             case LIST_TYPE_HIGH_POWER:
367                 return MetricsLogger.APPLICATIONS_HIGH_POWER_APPS;
368             default:
369                 return MetricsLogger.VIEW_UNKNOWN;
370         }
371     }
372
373     @Override
374     public void onResume() {
375         super.onResume();
376         updateView();
377         updateOptionsMenu();
378         if (mApplications != null) {
379             mApplications.resume(mSortOrder);
380             mApplications.updateLoading();
381         }
382     }
383
384     @Override
385     public void onSaveInstanceState(Bundle outState) {
386         super.onSaveInstanceState(outState);
387         mResetAppsHelper.onSaveInstanceState(outState);
388         outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
389     }
390
391     @Override
392     public void onPause() {
393         super.onPause();
394         if (mApplications != null) {
395             mApplications.pause();
396         }
397     }
398
399     @Override
400     public void onStop() {
401         super.onStop();
402         mResetAppsHelper.stop();
403     }
404
405     @Override
406     public void onDestroyView() {
407         super.onDestroyView();
408
409         if (mApplications != null) {
410             mApplications.release();
411         }
412         mRootView = null;
413     }
414
415     @Override
416     public void onActivityResult(int requestCode, int resultCode, Intent data) {
417         if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
418             if (mListType == LIST_TYPE_NOTIFICATION) {
419                 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
420             } else {
421                 mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid));
422             }
423         }
424     }
425
426     // utility method used to start sub activity
427     private void startApplicationDetailsActivity() {
428         switch (mListType) {
429             case LIST_TYPE_NOTIFICATION:
430                 startAppInfoFragment(AppNotificationSettings.class,
431                         R.string.app_notifications_title);
432                 break;
433             case LIST_TYPE_DOMAINS_URLS:
434                 startAppInfoFragment(AppLaunchSettings.class, R.string.auto_launch_label);
435                 break;
436             case LIST_TYPE_USAGE_ACCESS:
437                 startAppInfoFragment(UsageAccessDetails.class, R.string.usage_access);
438                 break;
439             case LIST_TYPE_STORAGE:
440                 startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings);
441                 break;
442             case LIST_TYPE_HIGH_POWER:
443                 startAppInfoFragment(HighPowerDetail.class, R.string.high_power_apps);
444                 break;
445             // TODO: Figure out if there is a way where we can spin up the profile's settings
446             // process ahead of time, to avoid a long load of data when user clicks on a managed app.
447             // Maybe when they load the list of apps that contains managed profile apps.
448             default:
449                 startAppInfoFragment(InstalledAppDetails.class, R.string.application_info_label);
450                 break;
451         }
452     }
453
454     private void startAppInfoFragment(Class<?> fragment, int titleRes) {
455         AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this,
456                 INSTALLED_APP_DETAILS);
457     }
458
459     @Override
460     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
461         if (mListType == LIST_TYPE_DOMAINS_URLS) {
462             return;
463         }
464         HelpUtils.prepareHelpMenuItem(getActivity(), menu, mListType == LIST_TYPE_MAIN
465                 ? R.string.help_uri_apps : R.string.help_uri_notifications);
466         mOptionsMenu = menu;
467         inflater.inflate(R.menu.manage_apps, menu);
468         updateOptionsMenu();
469     }
470
471     @Override
472     public void onPrepareOptionsMenu(Menu menu) {
473         updateOptionsMenu();
474     }
475
476     @Override
477     public void onDestroyOptionsMenu() {
478         mOptionsMenu = null;
479     }
480
481     void updateOptionsMenu() {
482         if (mOptionsMenu == null) {
483             return;
484         }
485         mOptionsMenu.findItem(R.id.advanced).setVisible(mListType == LIST_TYPE_MAIN);
486
487         mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE
488                 && mSortOrder != R.id.sort_order_alpha);
489         mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE
490                 && mSortOrder != R.id.sort_order_size);
491
492         mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem);
493         mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem);
494     }
495
496     @Override
497     public boolean onOptionsItemSelected(MenuItem item) {
498         int menuId = item.getItemId();
499         switch(item.getItemId()) {
500             case R.id.sort_order_alpha:
501             case R.id.sort_order_size:
502                 mSortOrder = menuId;
503                 if (mApplications != null) {
504                     mApplications.rebuild(mSortOrder);
505                 }
506                 break;
507             case R.id.show_system:
508             case R.id.hide_system:
509                 mShowSystem = !mShowSystem;
510                 mApplications.rebuild(false);
511                 break;
512             case R.id.reset_app_preferences:
513                 mResetAppsHelper.buildResetDialog();
514                 return true;
515             case R.id.advanced:
516                 ((SettingsActivity) getActivity()).startPreferencePanel(
517                         AdvancedAppSettings.class.getName(), null, R.string.advanced_apps,
518                         null, this, ADVANCED_SETTINGS);
519                 return true;
520             default:
521                 // Handle the home button
522                 return false;
523         }
524         updateOptionsMenu();
525         return true;
526     }
527
528     @Override
529     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
530         if (mApplications != null && mApplications.getCount() > position) {
531             ApplicationsState.AppEntry entry = mApplications.getAppEntry(position);
532             mCurrentPkgName = entry.info.packageName;
533             mCurrentUid = entry.info.uid;
534             startApplicationDetailsActivity();
535         }
536     }
537
538     @Override
539     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
540         mFilter = mFilterAdapter.getFilter(position);
541         mApplications.setFilter(mFilter);
542         if (DEBUG) Log.d(TAG, "Selecting filter " + mFilter);
543     }
544
545     @Override
546     public void onNothingSelected(AdapterView<?> parent) {
547     }
548
549     public void updateView() {
550         updateOptionsMenu();
551         final Activity host = getActivity();
552         if (host != null) {
553             host.invalidateOptionsMenu();
554         }
555     }
556
557     public void setHasDisabled(boolean hasDisabledApps) {
558         mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps);
559         mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps);
560     }
561
562     static class FilterSpinnerAdapter extends ArrayAdapter<CharSequence> {
563
564         private final ManageApplications mManageApplications;
565
566         // Use ArrayAdapter for view logic, but have our own list for managing
567         // the options available.
568         private final ArrayList<Integer> mFilterOptions = new ArrayList<>();
569
570         public FilterSpinnerAdapter(ManageApplications manageApplications) {
571             super(manageApplications.getActivity(), R.layout.filter_spinner_item);
572             setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
573             mManageApplications = manageApplications;
574         }
575
576         public int getFilter(int position) {
577             return mFilterOptions.get(position);
578         }
579
580         public void setFilterEnabled(int filter, boolean enabled) {
581             if (enabled) {
582                 enableFilter(filter);
583             } else {
584                 disableFilter(filter);
585             }
586         }
587
588         public void enableFilter(int filter) {
589             if (mFilterOptions.contains(filter)) return;
590             if (DEBUG) Log.d(TAG, "Enabling filter " + filter);
591             mFilterOptions.add(filter);
592             Collections.sort(mFilterOptions);
593             mManageApplications.mSpinnerHeader.setVisibility(
594                     mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
595             notifyDataSetChanged();
596             if (mFilterOptions.size() == 1) {
597                 if (DEBUG) Log.d(TAG, "Auto selecting filter " + filter);
598                 mManageApplications.mFilterSpinner.setSelection(0);
599                 mManageApplications.onItemSelected(null, null, 0, 0);
600             }
601         }
602
603         public void disableFilter(int filter) {
604             if (!mFilterOptions.remove((Integer) filter)) {
605                 return;
606             }
607             if (DEBUG) Log.d(TAG, "Disabling filter " + filter);
608             Collections.sort(mFilterOptions);
609             mManageApplications.mSpinnerHeader.setVisibility(
610                     mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
611             notifyDataSetChanged();
612             if (mManageApplications.mFilter == filter) {
613                 if (mFilterOptions.size() > 0) {
614                     if (DEBUG) Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0));
615                     mManageApplications.mFilterSpinner.setSelection(0);
616                     mManageApplications.onItemSelected(null, null, 0, 0);
617                 }
618             }
619         }
620
621         @Override
622         public int getCount() {
623             return mFilterOptions.size();
624         }
625
626         @Override
627         public CharSequence getItem(int position) {
628             return getFilterString(mFilterOptions.get(position));
629         }
630
631         private CharSequence getFilterString(int filter) {
632             return mManageApplications.getString(FILTER_LABELS[filter]);
633         }
634
635     }
636
637     /*
638      * Custom adapter implementation for the ListView
639      * This adapter maintains a map for each displayed application and its properties
640      * An index value on each AppInfo object indicates the correct position or index
641      * in the list. If the list gets updated dynamically when the user is viewing the list of
642      * applications, we need to return the correct index of position. This is done by mapping
643      * the getId methods via the package name into the internal maps and indices.
644      * The order of applications in the list is mirrored in mAppLocalList
645      */
646     static class ApplicationsAdapter extends BaseAdapter implements Filterable,
647             ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
648             AbsListView.RecyclerListener {
649         private final ApplicationsState mState;
650         private final ApplicationsState.Session mSession;
651         private final ManageApplications mManageApplications;
652         private final Context mContext;
653         private final ArrayList<View> mActive = new ArrayList<View>();
654         private final AppStateBaseBridge mExtraInfoBridge;
655         private int mFilterMode;
656         private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
657         private ArrayList<ApplicationsState.AppEntry> mEntries;
658         private boolean mResumed;
659         private int mLastSortMode=-1;
660         private int mWhichSize = SIZE_TOTAL;
661         CharSequence mCurFilterPrefix;
662         private PackageManager mPm;
663         private AppFilter mOverrideFilter;
664
665         private Filter mFilter = new Filter() {
666             @Override
667             protected FilterResults performFiltering(CharSequence constraint) {
668                 ArrayList<ApplicationsState.AppEntry> entries
669                         = applyPrefixFilter(constraint, mBaseEntries);
670                 FilterResults fr = new FilterResults();
671                 fr.values = entries;
672                 fr.count = entries.size();
673                 return fr;
674             }
675
676             @Override
677             @SuppressWarnings("unchecked")
678             protected void publishResults(CharSequence constraint, FilterResults results) {
679                 mCurFilterPrefix = constraint;
680                 mEntries = (ArrayList<ApplicationsState.AppEntry>) results.values;
681                 notifyDataSetChanged();
682             }
683         };
684
685         public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
686                 int filterMode) {
687             mState = state;
688             mSession = state.newSession(this);
689             mManageApplications = manageApplications;
690             mContext = manageApplications.getActivity();
691             mPm = mContext.getPackageManager();
692             mFilterMode = filterMode;
693             if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
694                 mExtraInfoBridge = new AppStateNotificationBridge(mContext.getPackageManager(),
695                         mState, this, manageApplications.mNotifBackend);
696             } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
697                 mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
698             } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) {
699                 mExtraInfoBridge = new AppStatePowerBridge(mState, this);
700             } else {
701                 mExtraInfoBridge = null;
702             }
703         }
704
705         public void setOverrideFilter(AppFilter overrideFilter) {
706             mOverrideFilter = overrideFilter;
707             rebuild(true);
708         }
709
710         public void setFilter(int filter) {
711             mFilterMode = filter;
712             rebuild(true);
713         }
714
715         public void resume(int sort) {
716             if (DEBUG) Log.i(TAG, "Resume!  mResumed=" + mResumed);
717             if (!mResumed) {
718                 mResumed = true;
719                 mSession.resume();
720                 mLastSortMode = sort;
721                 if (mExtraInfoBridge != null) {
722                     mExtraInfoBridge.resume();
723                 }
724                 rebuild(true);
725             } else {
726                 rebuild(sort);
727             }
728         }
729
730         public void pause() {
731             if (mResumed) {
732                 mResumed = false;
733                 mSession.pause();
734                 if (mExtraInfoBridge != null) {
735                     mExtraInfoBridge.pause();
736                 }
737             }
738         }
739
740         public void release() {
741             mSession.release();
742             if (mExtraInfoBridge != null) {
743                 mExtraInfoBridge.release();
744             }
745         }
746
747         public void rebuild(int sort) {
748             if (sort == mLastSortMode) {
749                 return;
750             }
751             mLastSortMode = sort;
752             rebuild(true);
753         }
754
755         public void rebuild(boolean eraseold) {
756             if (DEBUG) Log.i(TAG, "Rebuilding app list...");
757             ApplicationsState.AppFilter filterObj;
758             Comparator<AppEntry> comparatorObj;
759             boolean emulated = Environment.isExternalStorageEmulated();
760             if (emulated) {
761                 mWhichSize = SIZE_TOTAL;
762             } else {
763                 mWhichSize = SIZE_INTERNAL;
764             }
765             filterObj = FILTERS[mFilterMode];
766             if (mOverrideFilter != null) {
767                 filterObj = mOverrideFilter;
768             }
769             if (!mManageApplications.mShowSystem) {
770                 filterObj = new CompoundFilter(filterObj,
771                         ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
772             }
773             switch (mLastSortMode) {
774                 case R.id.sort_order_size:
775                     switch (mWhichSize) {
776                         case SIZE_INTERNAL:
777                             comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
778                             break;
779                         case SIZE_EXTERNAL:
780                             comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
781                             break;
782                         default:
783                             comparatorObj = ApplicationsState.SIZE_COMPARATOR;
784                             break;
785                     }
786                     break;
787                 default:
788                     comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
789                     break;
790             }
791             ArrayList<ApplicationsState.AppEntry> entries
792                     = mSession.rebuild(filterObj, comparatorObj);
793             if (entries == null && !eraseold) {
794                 // Don't have new list yet, but can continue using the old one.
795                 return;
796             }
797             mBaseEntries = entries;
798             if (mBaseEntries != null) {
799                 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
800             } else {
801                 mEntries = null;
802             }
803             notifyDataSetChanged();
804
805             if (mSession.getAllApps().size() != 0
806                     && mManageApplications.mListContainer.getVisibility() != View.VISIBLE) {
807                 Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
808                         mManageApplications.mListContainer, true, true);
809             }
810             if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
811                 // No enabled or disabled filters for usage access.
812                 return;
813             }
814
815             mManageApplications.setHasDisabled(mState.haveDisabledApps());
816         }
817
818         private void updateLoading() {
819             Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
820                     mManageApplications.mListContainer, mSession.getAllApps().size() != 0, false);
821         }
822
823         private boolean hasDisabledApps() {
824             ArrayList<AppEntry> allApps = mSession.getAllApps();
825             for (int i = 0; i < allApps.size(); i++) {
826                 if (!allApps.get(i).info.enabled) {
827                     return true;
828                 }
829             }
830             return false;
831         }
832
833         ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
834                 ArrayList<ApplicationsState.AppEntry> origEntries) {
835             if (prefix == null || prefix.length() == 0) {
836                 return origEntries;
837             } else {
838                 String prefixStr = ApplicationsState.normalize(prefix.toString());
839                 final String spacePrefixStr = " " + prefixStr;
840                 ArrayList<ApplicationsState.AppEntry> newEntries
841                         = new ArrayList<ApplicationsState.AppEntry>();
842                 for (int i=0; i<origEntries.size(); i++) {
843                     ApplicationsState.AppEntry entry = origEntries.get(i);
844                     String nlabel = entry.getNormalizedLabel();
845                     if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
846                         newEntries.add(entry);
847                     }
848                 }
849                 return newEntries;
850             }
851         }
852
853         @Override
854         public void onExtraInfoUpdated() {
855             rebuild(false);
856         }
857
858         @Override
859         public void onRunningStateChanged(boolean running) {
860             mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running);
861         }
862
863         @Override
864         public void onRebuildComplete(ArrayList<AppEntry> apps) {
865             if (mManageApplications.mLoadingContainer.getVisibility() == View.VISIBLE) {
866                 mManageApplications.mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
867                         mContext, android.R.anim.fade_out));
868                 mManageApplications.mListContainer.startAnimation(AnimationUtils.loadAnimation(
869                         mContext, android.R.anim.fade_in));
870             }
871             mManageApplications.mListContainer.setVisibility(View.VISIBLE);
872             mManageApplications.mLoadingContainer.setVisibility(View.GONE);
873             mBaseEntries = apps;
874             mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
875             notifyDataSetChanged();
876         }
877
878         @Override
879         public void onPackageListChanged() {
880             rebuild(false);
881         }
882
883         @Override
884         public void onPackageIconChanged() {
885             // We ensure icons are loaded when their item is displayed, so
886             // don't care about icons loaded in the background.
887         }
888
889         @Override
890         public void onLoadEntriesCompleted() {
891             // No op.
892         }
893
894         @Override
895         public void onPackageSizeChanged(String packageName) {
896             for (int i=0; i<mActive.size(); i++) {
897                 AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag();
898                 if (holder.entry.info.packageName.equals(packageName)) {
899                     synchronized (holder.entry) {
900                         updateSummary(holder);
901                     }
902                     if (holder.entry.info.packageName.equals(mManageApplications.mCurrentPkgName)
903                             && mLastSortMode == R.id.sort_order_size) {
904                         // We got the size information for the last app the
905                         // user viewed, and are sorting by size...  they may
906                         // have cleared data, so we immediately want to resort
907                         // the list with the new size to reflect it to the user.
908                         rebuild(false);
909                     }
910                     return;
911                 }
912             }
913         }
914
915         @Override
916         public void onLauncherInfoChanged() {
917             if (!mManageApplications.mShowSystem) {
918                 rebuild(false);
919             }
920         }
921
922         @Override
923         public void onAllSizesComputed() {
924             if (mLastSortMode == R.id.sort_order_size) {
925                 rebuild(false);
926             }
927         }
928
929         public int getCount() {
930             return mEntries != null ? mEntries.size() : 0;
931         }
932
933         public Object getItem(int position) {
934             return mEntries.get(position);
935         }
936
937         public ApplicationsState.AppEntry getAppEntry(int position) {
938             return mEntries.get(position);
939         }
940
941         public long getItemId(int position) {
942             return mEntries.get(position).id;
943         }
944
945         @Override
946         public boolean areAllItemsEnabled() {
947             return false;
948         }
949
950         public View getView(int position, View convertView, ViewGroup parent) {
951             // A ViewHolder keeps references to children views to avoid unnecessary calls
952             // to findViewById() on each row.
953             AppViewHolder holder = AppViewHolder.createOrRecycle(mManageApplications.mInflater,
954                     convertView);
955             convertView = holder.rootView;
956
957             // Bind the data efficiently with the holder
958             ApplicationsState.AppEntry entry = mEntries.get(position);
959             synchronized (entry) {
960                 holder.entry = entry;
961                 if (entry.label != null) {
962                     holder.appName.setText(entry.label);
963                 }
964                 mState.ensureIcon(entry);
965                 if (entry.icon != null) {
966                     holder.appIcon.setImageDrawable(entry.icon);
967                 }
968                 updateSummary(holder);
969                 if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
970                     holder.disabled.setVisibility(View.VISIBLE);
971                     holder.disabled.setText(R.string.not_installed);
972                 } else if (!entry.info.enabled) {
973                     holder.disabled.setVisibility(View.VISIBLE);
974                     holder.disabled.setText(R.string.disabled);
975                 } else {
976                     holder.disabled.setVisibility(View.GONE);
977                 }
978                 holder.checkBox.setVisibility(View.GONE);
979             }
980             mActive.remove(convertView);
981             mActive.add(convertView);
982             return convertView;
983         }
984
985         private void updateSummary(AppViewHolder holder) {
986             switch (mManageApplications.mListType) {
987                 case LIST_TYPE_NOTIFICATION:
988                     if (holder.entry.extraInfo != null) {
989                         holder.summary.setText(InstalledAppDetails.getNotificationSummary(
990                                 (AppRow) holder.entry.extraInfo, mContext));
991                     } else {
992                         holder.summary.setText(null);
993                     }
994                     break;
995
996                 case LIST_TYPE_DOMAINS_URLS:
997                     holder.summary.setText(getDomainsSummary(holder.entry.info.packageName));
998                     break;
999
1000                 case LIST_TYPE_USAGE_ACCESS:
1001                     if (holder.entry.extraInfo != null) {
1002                         holder.summary.setText(((UsageState) holder.entry.extraInfo).hasAccess() ?
1003                                 R.string.switch_on_text : R.string.switch_off_text);
1004                     } else {
1005                         holder.summary.setText(null);
1006                     }
1007                     break;
1008
1009                 case LIST_TYPE_HIGH_POWER:
1010                     holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry));
1011                     break;
1012
1013                 default:
1014                     holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
1015                     break;
1016             }
1017         }
1018
1019         @Override
1020         public Filter getFilter() {
1021             return mFilter;
1022         }
1023
1024         @Override
1025         public void onMovedToScrapHeap(View view) {
1026             mActive.remove(view);
1027         }
1028
1029         private CharSequence getDomainsSummary(String packageName) {
1030             ArraySet<String> result = new ArraySet<>();
1031             List<IntentFilterVerificationInfo> list =
1032                     mPm.getIntentFilterVerifications(packageName);
1033             for (IntentFilterVerificationInfo ivi : list) {
1034                 for (String host : ivi.getDomains()) {
1035                     result.add(host);
1036                 }
1037             }
1038             if (result.size() == 0) {
1039                 return mContext.getString(R.string.domain_urls_summary_none);
1040             } else if (result.size() == 1) {
1041                 return mContext.getString(R.string.domain_urls_summary_one, result.valueAt(0));
1042             } else {
1043                 return mContext.getString(R.string.domain_urls_summary_some, result.valueAt(0));
1044             }
1045         }
1046     }
1047 }