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.annotation.IdRes;
20 import android.annotation.Nullable;
21 import android.app.Activity;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageItemInfo;
26 import android.content.pm.PackageManager;
27 import android.icu.text.AlphabeticIndex;
28 import android.os.Bundle;
29 import android.os.Environment;
30 import android.os.Handler;
31 import android.os.LocaleList;
32 import android.os.UserHandle;
33 import android.os.UserManager;
34 import android.preference.PreferenceFrameLayout;
35 import android.support.annotation.VisibleForTesting;
36 import android.text.TextUtils;
37 import android.util.ArraySet;
38 import android.util.Log;
39 import android.view.LayoutInflater;
40 import android.view.Menu;
41 import android.view.MenuInflater;
42 import android.view.MenuItem;
43 import android.view.View;
44 import android.view.ViewGroup;
45 import android.widget.AbsListView;
46 import android.widget.AdapterView;
47 import android.widget.AdapterView.OnItemClickListener;
48 import android.widget.AdapterView.OnItemSelectedListener;
49 import android.widget.ArrayAdapter;
50 import android.widget.BaseAdapter;
51 import android.widget.Filter;
52 import android.widget.Filterable;
53 import android.widget.FrameLayout;
54 import android.widget.ListView;
55 import android.widget.SectionIndexer;
56 import android.widget.Spinner;
57 import android.widget.TextView;
58 
59 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
60 import com.android.settings.R;
61 import com.android.settings.Settings.AllApplicationsActivity;
62 import com.android.settings.Settings.GamesStorageActivity;
63 import com.android.settings.Settings.HighPowerApplicationsActivity;
64 import com.android.settings.Settings.ManageExternalSourcesActivity;
65 import com.android.settings.Settings.MoviesStorageActivity;
66 import com.android.settings.Settings.NotificationAppListActivity;
67 import com.android.settings.Settings.OverlaySettingsActivity;
68 import com.android.settings.Settings.StorageUseActivity;
69 import com.android.settings.Settings.UsageAccessSettingsActivity;
70 import com.android.settings.Settings.WriteSettingsActivity;
71 import com.android.settings.SettingsActivity;
72 import com.android.settings.Utils;
73 import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
74 import com.android.settings.applications.AppStateUsageBridge.UsageState;
75 import com.android.settings.core.InstrumentedPreferenceFragment;
76 import com.android.settings.dashboard.SummaryLoader;
77 import com.android.settings.fuelgauge.HighPowerDetail;
78 import com.android.settings.fuelgauge.PowerWhitelistBackend;
79 import com.android.settings.notification.AppNotificationSettings;
80 import com.android.settings.notification.ConfigureNotificationSettings;
81 import com.android.settings.notification.NotificationBackend;
82 import com.android.settings.notification.NotificationBackend.AppRow;
83 import com.android.settingslib.HelpUtils;
84 import com.android.settingslib.applications.ApplicationsState;
85 import com.android.settingslib.applications.ApplicationsState.AppEntry;
86 import com.android.settingslib.applications.ApplicationsState.AppFilter;
87 import com.android.settingslib.applications.ApplicationsState.CompoundFilter;
88 import com.android.settingslib.applications.ApplicationsState.VolumeFilter;
89 import com.android.settingslib.applications.StorageStatsSource;
90 
91 import java.util.ArrayList;
92 import java.util.Arrays;
93 import java.util.Collections;
94 import java.util.Comparator;
95 import java.util.Locale;
96 import java.util.Set;
97 
98 /**
99  * Activity to pick an application that will be used to display installation information and
100  * options to uninstall/delete user data for system applications. This activity
101  * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
102  * intent.
103  */
104 public class ManageApplications extends InstrumentedPreferenceFragment
105         implements OnItemClickListener, OnItemSelectedListener {
106 
107     static final String TAG = "ManageApplications";
108     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
109 
110     // Intent extras.
111     public static final String EXTRA_CLASSNAME = "classname";
112     // Used for storage only.
113     public static final String EXTRA_VOLUME_UUID = "volumeUuid";
114     public static final String EXTRA_VOLUME_NAME = "volumeName";
115     public static final String EXTRA_STORAGE_TYPE = "storageType";
116 
117     private static final String EXTRA_SORT_ORDER = "sortOrder";
118     private static final String EXTRA_SHOW_SYSTEM = "showSystem";
119     private static final String EXTRA_HAS_ENTRIES = "hasEntries";
120     private static final String EXTRA_HAS_BRIDGE = "hasBridge";
121 
122     // attributes used as keys when passing values to InstalledAppDetails activity
123     public static final String APP_CHG = "chg";
124 
125     // constant value that can be used to check return code from sub activity.
126     private static final int INSTALLED_APP_DETAILS = 1;
127     private static final int ADVANCED_SETTINGS = 2;
128 
129     public static final int SIZE_TOTAL = 0;
130     public static final int SIZE_INTERNAL = 1;
131     public static final int SIZE_EXTERNAL = 2;
132 
133     // Filter options used for displayed list of applications
134     // Filters will appear sorted based on their value defined here.
135     public static final int FILTER_APPS_POWER_WHITELIST = 0;
136     public static final int FILTER_APPS_POWER_WHITELIST_ALL = 1;
137     public static final int FILTER_APPS_ALL = 2;
138     public static final int FILTER_APPS_ENABLED = 3;
139     public static final int FILTER_APPS_INSTANT = 4;
140     public static final int FILTER_APPS_DISABLED = 5;
141     public static final int FILTER_APPS_BLOCKED = 6;
142     public static final int FILTER_APPS_PERSONAL = 7;
143     public static final int FILTER_APPS_WORK = 8;
144     public static final int FILTER_APPS_USAGE_ACCESS = 9;
145     public static final int FILTER_APPS_WITH_OVERLAY = 10;
146     public static final int FILTER_APPS_WRITE_SETTINGS = 11;
147     public static final int FILTER_APPS_INSTALL_SOURCES = 12;
148     public static final int FILTER_APPS_COUNT = 13;  // This should always be the last entry
149 
150     // Mapping to string labels for the FILTER_APPS_* constants above.
151     public static final @IdRes int[] FILTER_LABELS = new int[FILTER_APPS_COUNT];
152 
153     // Mapping to filters for the FILTER_APPS_* constants above.
154     public static final AppFilter[] FILTERS = new AppFilter[FILTER_APPS_COUNT];
155 
156     static {
157         // High power whitelist, on
158         FILTER_LABELS[FILTER_APPS_POWER_WHITELIST] = R.string.high_power_filter_on;
159         FILTERS[FILTER_APPS_POWER_WHITELIST] = new CompoundFilter(
160                 AppStatePowerBridge.FILTER_POWER_WHITELISTED,
161                 ApplicationsState.FILTER_ALL_ENABLED);
162 
163         // Without disabled until used
164         FILTER_LABELS[FILTER_APPS_POWER_WHITELIST_ALL] = R.string.filter_all_apps;
165         FILTERS[FILTER_APPS_POWER_WHITELIST_ALL] = new CompoundFilter(
166                 ApplicationsState.FILTER_WITHOUT_DISABLED_UNTIL_USED,
167                 ApplicationsState.FILTER_ALL_ENABLED);
168 
169         // All apps
170         FILTER_LABELS[FILTER_APPS_ALL] = R.string.filter_all_apps;
171         FILTERS[FILTER_APPS_ALL] = ApplicationsState.FILTER_EVERYTHING;
172 
173         // Enabled
174         FILTER_LABELS[FILTER_APPS_ENABLED] = R.string.filter_enabled_apps;
175         FILTERS[FILTER_APPS_ENABLED] = ApplicationsState.FILTER_ALL_ENABLED;
176 
177         // Disabled
178         FILTER_LABELS[FILTER_APPS_DISABLED] = R.string.filter_apps_disabled;
179         FILTERS[FILTER_APPS_DISABLED] = ApplicationsState.FILTER_DISABLED;
180 
181         // Instant
182         FILTER_LABELS[FILTER_APPS_INSTANT] = R.string.filter_instant_apps;
183         FILTERS[FILTER_APPS_INSTANT] = ApplicationsState.FILTER_INSTANT;
184 
185         // Blocked Notifications
186         FILTER_LABELS[FILTER_APPS_BLOCKED] = R.string.filter_notif_blocked_apps;
187         FILTERS[FILTER_APPS_BLOCKED] = AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED;
188 
189         // Personal
190         FILTER_LABELS[FILTER_APPS_PERSONAL] = R.string.filter_personal_apps;
191         FILTERS[FILTER_APPS_PERSONAL] = ApplicationsState.FILTER_PERSONAL;
192 
193         // Work
194         FILTER_LABELS[FILTER_APPS_WORK] = R.string.filter_work_apps;
195         FILTERS[FILTER_APPS_WORK] = ApplicationsState.FILTER_WORK;
196 
197         // Usage access screen, never displayed.
198         FILTER_LABELS[FILTER_APPS_USAGE_ACCESS] = R.string.filter_all_apps;
199         FILTERS[FILTER_APPS_USAGE_ACCESS] = AppStateUsageBridge.FILTER_APP_USAGE;
200 
201         // Apps that can draw overlays
202         FILTER_LABELS[FILTER_APPS_WITH_OVERLAY] = R.string.filter_overlay_apps;
203         FILTERS[FILTER_APPS_WITH_OVERLAY] = AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW;
204 
205         // Apps that can write system settings
206         FILTER_LABELS[FILTER_APPS_WRITE_SETTINGS] = R.string.filter_write_settings_apps;
207         FILTERS[FILTER_APPS_WRITE_SETTINGS] = AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS;
208 
209         // Apps that are trusted sources of apks
210         FILTER_LABELS[FILTER_APPS_INSTALL_SOURCES] = R.string.filter_install_sources_apps;
211         FILTERS[FILTER_APPS_INSTALL_SOURCES] = AppStateInstallAppsBridge.FILTER_APP_SOURCES;
212     }
213 
214     // Storage types. Used to determine what the extra item in the list of preferences is.
215     public static final int STORAGE_TYPE_DEFAULT = 0;
216     public static final int STORAGE_TYPE_MUSIC = 1;
217 
218     // sort order
219     private int mSortOrder = R.id.sort_order_alpha;
220 
221     // whether showing system apps.
222     private boolean mShowSystem;
223 
224     private ApplicationsState mApplicationsState;
225 
226     public int mListType;
227     public int mFilter;
228 
229     public ApplicationsAdapter mApplications;
230 
231     private View mLoadingContainer;
232 
233     private View mListContainer;
234 
235     // ListView used to display list
236     private ListView mListView;
237 
238     // Size resource used for packages whose size computation failed for some reason
239     CharSequence mInvalidSizeStr;
240 
241     // layout inflater object used to inflate views
242     private LayoutInflater mInflater;
243 
244     private String mCurrentPkgName;
245     private int mCurrentUid;
246     private boolean mFinishAfterDialog;
247 
248     private Menu mOptionsMenu;
249 
250     public static final int LIST_TYPE_MAIN = 0;
251     public static final int LIST_TYPE_NOTIFICATION = 1;
252     public static final int LIST_TYPE_STORAGE = 3;
253     public static final int LIST_TYPE_USAGE_ACCESS = 4;
254     public static final int LIST_TYPE_HIGH_POWER = 5;
255     public static final int LIST_TYPE_OVERLAY = 6;
256     public static final int LIST_TYPE_WRITE_SETTINGS = 7;
257     public static final int LIST_TYPE_MANAGE_SOURCES = 8;
258     public static final int LIST_TYPE_GAMES = 9;
259     public static final int LIST_TYPE_MOVIES = 10;
260 
261 
262     // List types that should show instant apps.
263     public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList(
264                     LIST_TYPE_MAIN,
265                     LIST_TYPE_STORAGE));
266 
267     private View mRootView;
268 
269     private View mSpinnerHeader;
270     private Spinner mFilterSpinner;
271     private FilterSpinnerAdapter mFilterAdapter;
272     private NotificationBackend mNotifBackend;
273     private ResetAppsHelper mResetAppsHelper;
274     private String mVolumeUuid;
275     private int mStorageType;
276 
277     @Override
onCreate(Bundle savedInstanceState)278     public void onCreate(Bundle savedInstanceState) {
279         super.onCreate(savedInstanceState);
280         setHasOptionsMenu(true);
281         mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
282 
283         Intent intent = getActivity().getIntent();
284         Bundle args = getArguments();
285         String className = args != null ? args.getString(EXTRA_CLASSNAME) : null;
286         if (className == null) {
287             className = intent.getComponent().getClassName();
288         }
289         if (className.equals(AllApplicationsActivity.class.getName())) {
290             mShowSystem = true;
291         } else if (className.equals(NotificationAppListActivity.class.getName())
292                 || this instanceof NotificationApps) {
293             mListType = LIST_TYPE_NOTIFICATION;
294             mNotifBackend = new NotificationBackend();
295         } else if (className.equals(StorageUseActivity.class.getName())) {
296             if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) {
297                 mVolumeUuid = args.getString(EXTRA_VOLUME_UUID);
298                 mStorageType = args.getInt(EXTRA_STORAGE_TYPE, STORAGE_TYPE_DEFAULT);
299                 mListType = LIST_TYPE_STORAGE;
300             } else {
301                 // No volume selected, display a normal list, sorted by size.
302                 mListType = LIST_TYPE_MAIN;
303             }
304             mSortOrder = R.id.sort_order_size;
305         } else if (className.equals(UsageAccessSettingsActivity.class.getName())) {
306             mListType = LIST_TYPE_USAGE_ACCESS;
307         } else if (className.equals(HighPowerApplicationsActivity.class.getName())) {
308             mListType = LIST_TYPE_HIGH_POWER;
309             // Default to showing system.
310             mShowSystem = true;
311         } else if (className.equals(OverlaySettingsActivity.class.getName())) {
312             mListType = LIST_TYPE_OVERLAY;
313         } else if (className.equals(WriteSettingsActivity.class.getName())) {
314             mListType = LIST_TYPE_WRITE_SETTINGS;
315         } else if (className.equals(ManageExternalSourcesActivity.class.getName())) {
316             mListType = LIST_TYPE_MANAGE_SOURCES;
317         } else if (className.equals(GamesStorageActivity.class.getName())) {
318             mListType = LIST_TYPE_GAMES;
319             mSortOrder = R.id.sort_order_size;
320         } else if (className.equals(MoviesStorageActivity.class.getName())) {
321             mListType = LIST_TYPE_MOVIES;
322             mSortOrder = R.id.sort_order_size;
323         } else {
324             mListType = LIST_TYPE_MAIN;
325         }
326         mFilter = getDefaultFilter();
327 
328         if (savedInstanceState != null) {
329             mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
330             mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
331         }
332 
333         mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
334 
335         mResetAppsHelper = new ResetAppsHelper(getActivity());
336     }
337 
338 
339     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)340     public View onCreateView(LayoutInflater inflater, ViewGroup container,
341                              Bundle savedInstanceState) {
342         // initialize the inflater
343         mInflater = inflater;
344 
345         mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
346         mLoadingContainer = mRootView.findViewById(R.id.loading_container);
347         mLoadingContainer.setVisibility(View.VISIBLE);
348         mListContainer = mRootView.findViewById(R.id.list_container);
349         if (mListContainer != null) {
350             // Create adapter and list view here
351             View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
352             ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
353             if (emptyView != null) {
354                 lv.setEmptyView(emptyView);
355             }
356             lv.setOnItemClickListener(this);
357             lv.setSaveEnabled(true);
358             lv.setItemsCanFocus(true);
359             lv.setTextFilterEnabled(true);
360             mListView = lv;
361             mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
362             if (savedInstanceState != null) {
363                 mApplications.mHasReceivedLoadEntries =
364                         savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false);
365                 mApplications.mHasReceivedBridgeCallback =
366                         savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false);
367             }
368             if (mStorageType == STORAGE_TYPE_MUSIC) {
369                 Context context = getContext();
370                 mApplications.setExtraViewController(new MusicViewHolderController(
371                         context,
372                         new StorageStatsSource(context),
373                         mVolumeUuid,
374                         UserHandle.of(UserHandle.getUserId(mCurrentUid))));
375             }
376             mListView.setAdapter(mApplications);
377             mListView.setRecyclerListener(mApplications);
378             mListView.setFastScrollEnabled(isFastScrollEnabled());
379 
380             Utils.prepareCustomPreferencesList(container, mRootView, mListView, false);
381         }
382 
383         // We have to do this now because PreferenceFrameLayout looks at it
384         // only when the view is added.
385         if (container instanceof PreferenceFrameLayout) {
386             ((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true;
387         }
388 
389         createHeader();
390 
391         mResetAppsHelper.onRestoreInstanceState(savedInstanceState);
392 
393         return mRootView;
394     }
395 
createHeader()396     private void createHeader() {
397         Activity activity = getActivity();
398         FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
399         mSpinnerHeader = activity.getLayoutInflater()
400                 .inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
401         mFilterSpinner = (Spinner) mSpinnerHeader.findViewById(R.id.filter_spinner);
402         mFilterAdapter = new FilterSpinnerAdapter(this);
403         mFilterSpinner.setAdapter(mFilterAdapter);
404         mFilterSpinner.setOnItemSelectedListener(this);
405         pinnedHeader.addView(mSpinnerHeader, 0);
406 
407         mFilterAdapter.enableFilter(getDefaultFilter());
408         if (mListType == LIST_TYPE_MAIN) {
409             if (UserManager.get(getActivity()).getUserProfiles().size() > 1) {
410                 mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL);
411                 mFilterAdapter.enableFilter(FILTER_APPS_WORK);
412             }
413         }
414         if (mListType == LIST_TYPE_NOTIFICATION) {
415             mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED);
416         }
417         if (mListType == LIST_TYPE_HIGH_POWER) {
418             mFilterAdapter.enableFilter(FILTER_APPS_POWER_WHITELIST_ALL);
419         }
420 
421         AppFilter compositeFilter = getCompositeFilter(mListType, mStorageType, mVolumeUuid);
422         if (compositeFilter != null) {
423             mApplications.setCompositeFilter(compositeFilter);
424         }
425     }
426 
427     @VisibleForTesting
getCompositeFilter(int listType, int storageType, String volumeUuid)428     static @Nullable AppFilter getCompositeFilter(int listType, int storageType, String volumeUuid) {
429         AppFilter filter = new VolumeFilter(volumeUuid);
430         if (listType == LIST_TYPE_STORAGE) {
431             if (storageType == STORAGE_TYPE_MUSIC) {
432                 filter = new CompoundFilter(ApplicationsState.FILTER_AUDIO, filter);
433             } else {
434                 filter = new CompoundFilter(ApplicationsState.FILTER_OTHER_APPS, filter);
435             }
436             return filter;
437         }
438         if (listType == LIST_TYPE_GAMES) {
439             return new CompoundFilter(ApplicationsState.FILTER_GAMES, filter);
440         } else if (listType == LIST_TYPE_MOVIES) {
441             return new CompoundFilter(ApplicationsState.FILTER_MOVIES, filter);
442         }
443 
444         return null;
445     }
446 
getDefaultFilter()447     private int getDefaultFilter() {
448         switch (mListType) {
449             case LIST_TYPE_USAGE_ACCESS:
450                 return FILTER_APPS_USAGE_ACCESS;
451             case LIST_TYPE_HIGH_POWER:
452                 return FILTER_APPS_POWER_WHITELIST;
453             case LIST_TYPE_OVERLAY:
454                 return FILTER_APPS_WITH_OVERLAY;
455             case LIST_TYPE_WRITE_SETTINGS:
456                 return FILTER_APPS_WRITE_SETTINGS;
457             case LIST_TYPE_MANAGE_SOURCES:
458                 return FILTER_APPS_INSTALL_SOURCES;
459             default:
460                 return FILTER_APPS_ALL;
461         }
462     }
463 
isFastScrollEnabled()464     private boolean isFastScrollEnabled() {
465         switch (mListType) {
466             case LIST_TYPE_MAIN:
467             case LIST_TYPE_NOTIFICATION:
468             case LIST_TYPE_STORAGE:
469             case LIST_TYPE_GAMES:
470             case LIST_TYPE_MOVIES:
471                 return mSortOrder == R.id.sort_order_alpha;
472             default:
473                 return false;
474         }
475     }
476 
477     @Override
getMetricsCategory()478     public int getMetricsCategory() {
479         switch (mListType) {
480             case LIST_TYPE_MAIN:
481                 return MetricsEvent.MANAGE_APPLICATIONS;
482             case LIST_TYPE_NOTIFICATION:
483                 return MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS;
484             case LIST_TYPE_STORAGE:
485                 if (mStorageType == STORAGE_TYPE_MUSIC) {
486                     return MetricsEvent.APPLICATIONS_STORAGE_MUSIC;
487                 }
488                 return MetricsEvent.APPLICATIONS_STORAGE_APPS;
489             case LIST_TYPE_GAMES:
490                 return MetricsEvent.APPLICATIONS_STORAGE_GAMES;
491             case LIST_TYPE_MOVIES:
492                 return MetricsEvent.APPLICATIONS_STORAGE_MOVIES;
493             case LIST_TYPE_USAGE_ACCESS:
494                 return MetricsEvent.USAGE_ACCESS;
495             case LIST_TYPE_HIGH_POWER:
496                 return MetricsEvent.APPLICATIONS_HIGH_POWER_APPS;
497             case LIST_TYPE_OVERLAY:
498                 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
499             case LIST_TYPE_WRITE_SETTINGS:
500                 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
501             case LIST_TYPE_MANAGE_SOURCES:
502                 return MetricsEvent.MANAGE_EXTERNAL_SOURCES;
503             default:
504                 return MetricsEvent.VIEW_UNKNOWN;
505         }
506     }
507 
508     @Override
onResume()509     public void onResume() {
510         super.onResume();
511         updateView();
512         updateOptionsMenu();
513         if (mApplications != null) {
514             mApplications.resume(mSortOrder);
515             mApplications.updateLoading();
516         }
517     }
518 
519     @Override
onSaveInstanceState(Bundle outState)520     public void onSaveInstanceState(Bundle outState) {
521         super.onSaveInstanceState(outState);
522         mResetAppsHelper.onSaveInstanceState(outState);
523         outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
524         outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
525         outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries);
526         outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback);
527     }
528 
529     @Override
onPause()530     public void onPause() {
531         super.onPause();
532         if (mApplications != null) {
533             mApplications.pause();
534         }
535     }
536 
537     @Override
onStop()538     public void onStop() {
539         super.onStop();
540         mResetAppsHelper.stop();
541     }
542 
543     @Override
onDestroyView()544     public void onDestroyView() {
545         super.onDestroyView();
546 
547         if (mApplications != null) {
548             mApplications.release();
549         }
550         mRootView = null;
551     }
552 
553     @Override
onActivityResult(int requestCode, int resultCode, Intent data)554     public void onActivityResult(int requestCode, int resultCode, Intent data) {
555         if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
556             if (mListType == LIST_TYPE_NOTIFICATION) {
557                 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
558             } else if (mListType == LIST_TYPE_HIGH_POWER || mListType == LIST_TYPE_OVERLAY
559                     || mListType == LIST_TYPE_WRITE_SETTINGS) {
560                 if (mFinishAfterDialog) {
561                     getActivity().onBackPressed();
562                 } else {
563                     mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
564                 }
565             } else {
566                 mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid));
567             }
568         }
569     }
570 
571     // utility method used to start sub activity
startApplicationDetailsActivity()572     private void startApplicationDetailsActivity() {
573         switch (mListType) {
574             case LIST_TYPE_NOTIFICATION:
575                 startAppInfoFragment(AppNotificationSettings.class,
576                         R.string.app_notifications_title);
577                 break;
578             case LIST_TYPE_USAGE_ACCESS:
579                 startAppInfoFragment(UsageAccessDetails.class, R.string.usage_access);
580                 break;
581             case LIST_TYPE_STORAGE:
582                 startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings);
583                 break;
584             case LIST_TYPE_HIGH_POWER:
585                 HighPowerDetail.show(this, mCurrentPkgName, INSTALLED_APP_DETAILS,
586                         mFinishAfterDialog);
587                 break;
588             case LIST_TYPE_OVERLAY:
589                 startAppInfoFragment(DrawOverlayDetails.class, R.string.overlay_settings);
590                 break;
591             case LIST_TYPE_WRITE_SETTINGS:
592                 startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings);
593                 break;
594             case LIST_TYPE_MANAGE_SOURCES:
595                 startAppInfoFragment(ExternalSourcesDetails.class, R.string.install_other_apps);
596                 break;
597             case LIST_TYPE_GAMES:
598                 startAppInfoFragment(AppStorageSettings.class, R.string.game_storage_settings);
599                 break;
600             case LIST_TYPE_MOVIES:
601                 startAppInfoFragment(AppStorageSettings.class, R.string.storage_movies_tv);
602                 break;
603             // TODO: Figure out if there is a way where we can spin up the profile's settings
604             // process ahead of time, to avoid a long load of data when user clicks on a managed app.
605             // Maybe when they load the list of apps that contains managed profile apps.
606             default:
607                 startAppInfoFragment(InstalledAppDetails.class, R.string.application_info_label);
608                 break;
609         }
610     }
611 
startAppInfoFragment(Class<?> fragment, int titleRes)612     private void startAppInfoFragment(Class<?> fragment, int titleRes) {
613         AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this,
614                 INSTALLED_APP_DETAILS, getMetricsCategory());
615     }
616 
617     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)618     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
619         HelpUtils.prepareHelpMenuItem(getActivity(), menu, mListType == LIST_TYPE_MAIN
620                 ? R.string.help_uri_apps : R.string.help_uri_notifications, getClass().getName());
621         mOptionsMenu = menu;
622         inflater.inflate(R.menu.manage_apps, menu);
623         updateOptionsMenu();
624     }
625 
626     @Override
onPrepareOptionsMenu(Menu menu)627     public void onPrepareOptionsMenu(Menu menu) {
628         updateOptionsMenu();
629     }
630 
631     @Override
onDestroyOptionsMenu()632     public void onDestroyOptionsMenu() {
633         mOptionsMenu = null;
634     }
635 
updateOptionsMenu()636     void updateOptionsMenu() {
637         if (mOptionsMenu == null) {
638             return;
639         }
640         final Context context = getActivity();
641         mOptionsMenu.findItem(R.id.advanced).setVisible(false);
642 
643         mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE
644                 && mSortOrder != R.id.sort_order_alpha);
645         mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE
646                 && mSortOrder != R.id.sort_order_size);
647 
648         mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem
649                 && mListType != LIST_TYPE_HIGH_POWER);
650         mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem
651                 && mListType != LIST_TYPE_HIGH_POWER);
652     }
653 
654     @Override
onOptionsItemSelected(MenuItem item)655     public boolean onOptionsItemSelected(MenuItem item) {
656         int menuId = item.getItemId();
657         switch (item.getItemId()) {
658             case R.id.sort_order_alpha:
659             case R.id.sort_order_size:
660                 mSortOrder = menuId;
661                 mListView.setFastScrollEnabled(isFastScrollEnabled());
662                 if (mApplications != null) {
663                     mApplications.rebuild(mSortOrder);
664                 }
665                 break;
666             case R.id.show_system:
667             case R.id.hide_system:
668                 mShowSystem = !mShowSystem;
669                 mApplications.rebuild(false);
670                 break;
671             case R.id.reset_app_preferences:
672                 mResetAppsHelper.buildResetDialog();
673                 return true;
674             case R.id.advanced:
675                 if (mListType == LIST_TYPE_NOTIFICATION) {
676                     ((SettingsActivity) getActivity()).startPreferencePanel(this,
677                             ConfigureNotificationSettings.class.getName(), null,
678                             R.string.configure_notification_settings, null, this, ADVANCED_SETTINGS);
679                 } else {
680                     ((SettingsActivity) getActivity()).startPreferencePanel(this,
681                             AdvancedAppSettings.class.getName(), null, R.string.configure_apps,
682                             null, this, ADVANCED_SETTINGS);
683                 }
684                 return true;
685             default:
686                 // Handle the home button
687                 return false;
688         }
689         updateOptionsMenu();
690         return true;
691     }
692 
693     @Override
onItemClick(AdapterView<?> parent, View view, int position, long id)694     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
695         if (mApplications == null) {
696             return;
697         }
698 
699         if (mApplications.getApplicationCount() > position) {
700             ApplicationsState.AppEntry entry = mApplications.getAppEntry(position);
701             mCurrentPkgName = entry.info.packageName;
702             mCurrentUid = entry.info.uid;
703             startApplicationDetailsActivity();
704         } else {
705             mApplications.mExtraViewController.onClick(this);
706         }
707     }
708 
709     @Override
onItemSelected(AdapterView<?> parent, View view, int position, long id)710     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
711         mFilter = mFilterAdapter.getFilter(position);
712         mApplications.setFilter(mFilter);
713         if (DEBUG) Log.d(TAG, "Selecting filter " + mFilter);
714     }
715 
716     @Override
onNothingSelected(AdapterView<?> parent)717     public void onNothingSelected(AdapterView<?> parent) {
718     }
719 
updateView()720     public void updateView() {
721         updateOptionsMenu();
722         final Activity host = getActivity();
723         if (host != null) {
724             host.invalidateOptionsMenu();
725         }
726     }
727 
setHasDisabled(boolean hasDisabledApps)728     public void setHasDisabled(boolean hasDisabledApps) {
729         if (mListType != LIST_TYPE_MAIN) {
730             return;
731         }
732         mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps);
733         mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps);
734     }
735 
setHasInstant(boolean haveInstantApps)736     public void setHasInstant(boolean haveInstantApps) {
737         if (LIST_TYPES_WITH_INSTANT.contains(mListType)) {
738             mFilterAdapter.setFilterEnabled(FILTER_APPS_INSTANT, haveInstantApps);
739         }
740     }
741 
742     static class FilterSpinnerAdapter extends ArrayAdapter<CharSequence> {
743 
744         private final ManageApplications mManageApplications;
745 
746         // Use ArrayAdapter for view logic, but have our own list for managing
747         // the options available.
748         private final ArrayList<Integer> mFilterOptions = new ArrayList<>();
749 
FilterSpinnerAdapter(ManageApplications manageApplications)750         public FilterSpinnerAdapter(ManageApplications manageApplications) {
751             super(manageApplications.mFilterSpinner.getContext(), R.layout.filter_spinner_item);
752             setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
753             mManageApplications = manageApplications;
754         }
755 
getFilter(int position)756         public int getFilter(int position) {
757             return mFilterOptions.get(position);
758         }
759 
setFilterEnabled(int filter, boolean enabled)760         public void setFilterEnabled(int filter, boolean enabled) {
761             if (enabled) {
762                 enableFilter(filter);
763             } else {
764                 disableFilter(filter);
765             }
766         }
767 
enableFilter(int filter)768         public void enableFilter(int filter) {
769             if (mFilterOptions.contains(filter)) return;
770             if (DEBUG) Log.d(TAG, "Enabling filter " + filter);
771             mFilterOptions.add(filter);
772             Collections.sort(mFilterOptions);
773             mManageApplications.mSpinnerHeader.setVisibility(
774                     mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
775             notifyDataSetChanged();
776             if (mFilterOptions.size() == 1) {
777                 if (DEBUG) Log.d(TAG, "Auto selecting filter " + filter);
778                 mManageApplications.mFilterSpinner.setSelection(0);
779                 mManageApplications.onItemSelected(null, null, 0, 0);
780             }
781         }
782 
disableFilter(int filter)783         public void disableFilter(int filter) {
784             if (!mFilterOptions.remove((Integer) filter)) {
785                 return;
786             }
787             if (DEBUG) Log.d(TAG, "Disabling filter " + filter);
788             Collections.sort(mFilterOptions);
789             mManageApplications.mSpinnerHeader.setVisibility(
790                     mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
791             notifyDataSetChanged();
792             if (mManageApplications.mFilter == filter) {
793                 if (mFilterOptions.size() > 0) {
794                     if (DEBUG) Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0));
795                     mManageApplications.mFilterSpinner.setSelection(0);
796                     mManageApplications.onItemSelected(null, null, 0, 0);
797                 }
798             }
799         }
800 
801         @Override
getCount()802         public int getCount() {
803             return mFilterOptions.size();
804         }
805 
806         @Override
getItem(int position)807         public CharSequence getItem(int position) {
808             return getFilterString(mFilterOptions.get(position));
809         }
810 
getFilterString(int filter)811         private CharSequence getFilterString(int filter) {
812             return mManageApplications.getString(FILTER_LABELS[filter]);
813         }
814 
815     }
816 
817     /*
818      * Custom adapter implementation for the ListView
819      * This adapter maintains a map for each displayed application and its properties
820      * An index value on each AppInfo object indicates the correct position or index
821      * in the list. If the list gets updated dynamically when the user is viewing the list of
822      * applications, we need to return the correct index of position. This is done by mapping
823      * the getId methods via the package name into the internal maps and indices.
824      * The order of applications in the list is mirrored in mAppLocalList
825      */
826     static class ApplicationsAdapter extends BaseAdapter implements Filterable,
827             ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
828             AbsListView.RecyclerListener, SectionIndexer {
829         private static final SectionInfo[] EMPTY_SECTIONS = new SectionInfo[0];
830 
831         private final ApplicationsState mState;
832         private final ApplicationsState.Session mSession;
833         private final ManageApplications mManageApplications;
834         private final Context mContext;
835         private final ArrayList<View> mActive = new ArrayList<View>();
836         private final AppStateBaseBridge mExtraInfoBridge;
837         private final Handler mBgHandler;
838         private final Handler mFgHandler;
839 
840         private int mFilterMode;
841         private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
842         private ArrayList<ApplicationsState.AppEntry> mEntries;
843         private boolean mResumed;
844         private int mLastSortMode = -1;
845         private int mWhichSize = SIZE_TOTAL;
846         CharSequence mCurFilterPrefix;
847         private PackageManager mPm;
848         private AppFilter mCompositeFilter;
849         private boolean mHasReceivedLoadEntries;
850         private boolean mHasReceivedBridgeCallback;
851         private FileViewHolderController mExtraViewController;
852 
853         // These two variables are used to remember and restore the last scroll position when this
854         // fragment is paused. We need this special handling because app entries are added gradually
855         // when we rebuild the list after the user made some changes, like uninstalling an app.
856         private int mLastIndex = -1;
857         private int mLastTop;
858 
859         private AlphabeticIndex.ImmutableIndex<Locale> mIndex;
860         private SectionInfo[] mSections = EMPTY_SECTIONS;
861         private int[] mPositionToSectionIndex;
862 
863         private Filter mFilter = new Filter() {
864             @Override
865             protected FilterResults performFiltering(CharSequence constraint) {
866                 ArrayList<ApplicationsState.AppEntry> entries
867                         = applyPrefixFilter(constraint, mBaseEntries);
868                 FilterResults fr = new FilterResults();
869                 fr.values = entries;
870                 fr.count = entries.size();
871                 return fr;
872             }
873 
874             @Override
875             @SuppressWarnings("unchecked")
876             protected void publishResults(CharSequence constraint, FilterResults results) {
877                 mCurFilterPrefix = constraint;
878                 mEntries = (ArrayList<ApplicationsState.AppEntry>) results.values;
879                 rebuildSections();
880                 notifyDataSetChanged();
881             }
882         };
883 
ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications, int filterMode)884         public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
885                                    int filterMode) {
886             mState = state;
887             mFgHandler = new Handler();
888             mBgHandler = new Handler(mState.getBackgroundLooper());
889             mSession = state.newSession(this);
890             mManageApplications = manageApplications;
891             mContext = manageApplications.getActivity();
892             mPm = mContext.getPackageManager();
893             mFilterMode = filterMode;
894             if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
895                 mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this,
896                         manageApplications.mNotifBackend);
897             } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
898                 mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
899             } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) {
900                 mExtraInfoBridge = new AppStatePowerBridge(mState, this);
901             } else if (mManageApplications.mListType == LIST_TYPE_OVERLAY) {
902                 mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this);
903             } else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) {
904                 mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this);
905             } else if (mManageApplications.mListType == LIST_TYPE_MANAGE_SOURCES) {
906                 mExtraInfoBridge = new AppStateInstallAppsBridge(mContext, mState, this);
907             } else {
908                 mExtraInfoBridge = null;
909             }
910         }
911 
setCompositeFilter(AppFilter compositeFilter)912         public void setCompositeFilter(AppFilter compositeFilter) {
913             mCompositeFilter = compositeFilter;
914             rebuild(true);
915         }
916 
setFilter(int filter)917         public void setFilter(int filter) {
918             mFilterMode = filter;
919             rebuild(true);
920         }
921 
setExtraViewController(FileViewHolderController extraViewController)922         public void setExtraViewController(FileViewHolderController extraViewController) {
923             mExtraViewController = extraViewController;
924             mBgHandler.post(() -> {
925                 mExtraViewController.queryStats();
926                 mFgHandler.post(() -> {
927                     onExtraViewCompleted();
928                 });
929             });
930         }
931 
resume(int sort)932         public void resume(int sort) {
933             if (DEBUG) Log.i(TAG, "Resume!  mResumed=" + mResumed);
934             if (!mResumed) {
935                 mResumed = true;
936                 mSession.resume();
937                 mLastSortMode = sort;
938                 if (mExtraInfoBridge != null) {
939                     mExtraInfoBridge.resume();
940                 }
941                 rebuild(false);
942             } else {
943                 rebuild(sort);
944             }
945         }
946 
pause()947         public void pause() {
948             if (mResumed) {
949                 mResumed = false;
950                 mSession.pause();
951                 if (mExtraInfoBridge != null) {
952                     mExtraInfoBridge.pause();
953                 }
954             }
955             // Record the current scroll position before pausing.
956             mLastIndex = mManageApplications.mListView.getFirstVisiblePosition();
957             View v = mManageApplications.mListView.getChildAt(0);
958             mLastTop = (v == null) ? 0 : (v.getTop() - mManageApplications.mListView.getPaddingTop());
959         }
960 
release()961         public void release() {
962             mSession.release();
963             if (mExtraInfoBridge != null) {
964                 mExtraInfoBridge.release();
965             }
966         }
967 
rebuild(int sort)968         public void rebuild(int sort) {
969             if (sort == mLastSortMode) {
970                 return;
971             }
972             mLastSortMode = sort;
973             rebuild(true);
974         }
975 
rebuild(boolean eraseold)976         public void rebuild(boolean eraseold) {
977             if (!mHasReceivedLoadEntries
978                     || (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) {
979                 // Don't rebuild the list until all the app entries are loaded.
980                 return;
981             }
982             ApplicationsState.AppFilter filterObj;
983             Comparator<AppEntry> comparatorObj;
984             boolean emulated = Environment.isExternalStorageEmulated();
985             if (emulated) {
986                 mWhichSize = SIZE_TOTAL;
987             } else {
988                 mWhichSize = SIZE_INTERNAL;
989             }
990             filterObj = FILTERS[mFilterMode];
991             if (mCompositeFilter != null) {
992                 filterObj = new CompoundFilter(filterObj, mCompositeFilter);
993             }
994             if (!mManageApplications.mShowSystem) {
995                 if (LIST_TYPES_WITH_INSTANT.contains(mManageApplications.mListType)) {
996                     filterObj = new CompoundFilter(filterObj,
997                             ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT);
998                 } else {
999                     filterObj = new CompoundFilter(filterObj,
1000                             ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
1001                 }
1002             }
1003             switch (mLastSortMode) {
1004                 case R.id.sort_order_size:
1005                     switch (mWhichSize) {
1006                         case SIZE_INTERNAL:
1007                             comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
1008                             break;
1009                         case SIZE_EXTERNAL:
1010                             comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
1011                             break;
1012                         default:
1013                             comparatorObj = ApplicationsState.SIZE_COMPARATOR;
1014                             break;
1015                     }
1016                     break;
1017                 default:
1018                     comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
1019                     break;
1020             }
1021 
1022             filterObj = new CompoundFilter(filterObj, ApplicationsState.FILTER_NOT_HIDE);
1023             AppFilter finalFilterObj = filterObj;
1024             mBgHandler.post(() -> {
1025                 final ArrayList<AppEntry> entries = mSession.rebuild(finalFilterObj,
1026                         comparatorObj, false);
1027                 if (entries != null) {
1028                     mFgHandler.post(() -> onRebuildComplete(entries));
1029                 }
1030             });
1031         }
1032 
1033 
packageNameEquals(PackageItemInfo info1, PackageItemInfo info2)1034         static private boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) {
1035             if (info1 == null || info2 == null) {
1036                 return false;
1037             }
1038             if (info1.packageName == null || info2.packageName == null) {
1039                 return false;
1040             }
1041             return info1.packageName.equals(info2.packageName);
1042         }
1043 
removeDuplicateIgnoringUser( ArrayList<ApplicationsState.AppEntry> entries)1044         private ArrayList<ApplicationsState.AppEntry> removeDuplicateIgnoringUser(
1045                 ArrayList<ApplicationsState.AppEntry> entries)
1046         {
1047             int size = entries.size();
1048             // returnList will not have more entries than entries
1049             ArrayList<ApplicationsState.AppEntry> returnEntries = new
1050                     ArrayList<ApplicationsState.AppEntry>(size);
1051 
1052             // assume appinfo of same package but different users are grouped together
1053             PackageItemInfo lastInfo = null;
1054             for (int i = 0; i < size; i++) {
1055                 AppEntry appEntry = entries.get(i);
1056                 PackageItemInfo info = appEntry.info;
1057                 if (!packageNameEquals(lastInfo, appEntry.info)) {
1058                     returnEntries.add(appEntry);
1059                 }
1060                 lastInfo = info;
1061             }
1062             returnEntries.trimToSize();
1063             return returnEntries;
1064         }
1065 
1066         @Override
onRebuildComplete(ArrayList<AppEntry> entries)1067         public void onRebuildComplete(ArrayList<AppEntry> entries) {
1068             if (mFilterMode == FILTER_APPS_POWER_WHITELIST ||
1069                     mFilterMode == FILTER_APPS_POWER_WHITELIST_ALL) {
1070                 entries = removeDuplicateIgnoringUser(entries);
1071             }
1072             mBaseEntries = entries;
1073             if (mBaseEntries != null) {
1074                 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
1075                 rebuildSections();
1076             } else {
1077                 mEntries = null;
1078                 mSections = EMPTY_SECTIONS;
1079                 mPositionToSectionIndex = null;
1080             }
1081 
1082             notifyDataSetChanged();
1083             // Restore the last scroll position if the number of entries added so far is bigger than
1084             // it.
1085             if (mLastIndex != -1 && getCount() > mLastIndex) {
1086                 mManageApplications.mListView.setSelectionFromTop(mLastIndex, mLastTop);
1087                 mLastIndex = -1;
1088             }
1089 
1090             if (mSession.getAllApps().size() != 0
1091                     && mManageApplications.mListContainer.getVisibility() != View.VISIBLE) {
1092                 Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
1093                         mManageApplications.mListContainer, true, true);
1094             }
1095             if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
1096                 // No enabled or disabled filters for usage access.
1097                 return;
1098             }
1099 
1100             mManageApplications.setHasDisabled(mState.haveDisabledApps());
1101             mManageApplications.setHasInstant(mState.haveInstantApps());
1102         }
1103 
rebuildSections()1104         private void rebuildSections() {
1105             if (mEntries!= null && mManageApplications.mListView.isFastScrollEnabled()) {
1106                 // Rebuild sections
1107                 if (mIndex == null) {
1108                     LocaleList locales = mContext.getResources().getConfiguration().getLocales();
1109                     if (locales.size() == 0) {
1110                         locales = new LocaleList(Locale.ENGLISH);
1111                     }
1112                     AlphabeticIndex<Locale> index = new AlphabeticIndex<>(locales.get(0));
1113                     int localeCount = locales.size();
1114                     for (int i = 1; i < localeCount; i++) {
1115                         index.addLabels(locales.get(i));
1116                     }
1117                     // Ensure we always have some base English locale buckets
1118                     index.addLabels(Locale.ENGLISH);
1119                     mIndex = index.buildImmutableIndex();
1120                 }
1121 
1122                 ArrayList<SectionInfo> sections = new ArrayList<>();
1123                 int lastSecId = -1;
1124                 int totalEntries = mEntries.size();
1125                 mPositionToSectionIndex = new int[totalEntries];
1126 
1127                 for (int pos = 0; pos < totalEntries; pos++) {
1128                     String label = mEntries.get(pos).label;
1129                     int secId = mIndex.getBucketIndex(TextUtils.isEmpty(label) ? "" : label);
1130                     if (secId != lastSecId) {
1131                         lastSecId = secId;
1132                         sections.add(new SectionInfo(mIndex.getBucket(secId).getLabel(), pos));
1133                     }
1134                     mPositionToSectionIndex[pos] = sections.size() - 1;
1135                 }
1136                 mSections = sections.toArray(EMPTY_SECTIONS);
1137             } else {
1138                 mSections = EMPTY_SECTIONS;
1139                 mPositionToSectionIndex = null;
1140             }
1141         }
1142 
updateLoading()1143         private void updateLoading() {
1144             Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
1145                     mManageApplications.mListContainer,
1146                     mHasReceivedLoadEntries && mSession.getAllApps().size() != 0, false);
1147         }
1148 
applyPrefixFilter(CharSequence prefix, ArrayList<ApplicationsState.AppEntry> origEntries)1149         ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
1150                                                                 ArrayList<ApplicationsState.AppEntry> origEntries) {
1151             if (prefix == null || prefix.length() == 0) {
1152                 return origEntries;
1153             } else {
1154                 String prefixStr = ApplicationsState.normalize(prefix.toString());
1155                 final String spacePrefixStr = " " + prefixStr;
1156                 ArrayList<ApplicationsState.AppEntry> newEntries
1157                         = new ArrayList<ApplicationsState.AppEntry>();
1158                 for (int i = 0; i < origEntries.size(); i++) {
1159                     ApplicationsState.AppEntry entry = origEntries.get(i);
1160                     String nlabel = entry.getNormalizedLabel();
1161                     if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
1162                         newEntries.add(entry);
1163                     }
1164                 }
1165                 return newEntries;
1166             }
1167         }
1168 
1169         @Override
onExtraInfoUpdated()1170         public void onExtraInfoUpdated() {
1171             mHasReceivedBridgeCallback = true;
1172             rebuild(false);
1173         }
1174 
1175         @Override
onRunningStateChanged(boolean running)1176         public void onRunningStateChanged(boolean running) {
1177             mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running);
1178         }
1179 
1180         @Override
onPackageListChanged()1181         public void onPackageListChanged() {
1182             rebuild(false);
1183         }
1184 
1185         @Override
onPackageIconChanged()1186         public void onPackageIconChanged() {
1187             // We ensure icons are loaded when their item is displayed, so
1188             // don't care about icons loaded in the background.
1189         }
1190 
1191         @Override
onLoadEntriesCompleted()1192         public void onLoadEntriesCompleted() {
1193             mHasReceivedLoadEntries = true;
1194             // We may have been skipping rebuilds until this came in, trigger one now.
1195             rebuild(false);
1196         }
1197 
1198         @Override
onPackageSizeChanged(String packageName)1199         public void onPackageSizeChanged(String packageName) {
1200             for (int i = 0; i < mActive.size(); i++) {
1201                 AppViewHolder holder = (AppViewHolder) mActive.get(i).getTag();
1202                 if (holder == null || holder.entry == null) {
1203                     continue;
1204                 }
1205                 ApplicationInfo info = holder.entry.info;
1206                 if (info == null) {
1207                     continue;
1208                 }
1209                 if (holder.entry.info.packageName.equals(packageName)) {
1210                     synchronized (holder.entry) {
1211                         updateSummary(holder);
1212                     }
1213                     if (holder.entry.info.packageName.equals(mManageApplications.mCurrentPkgName)
1214                             && mLastSortMode == R.id.sort_order_size) {
1215                         // We got the size information for the last app the
1216                         // user viewed, and are sorting by size...  they may
1217                         // have cleared data, so we immediately want to resort
1218                         // the list with the new size to reflect it to the user.
1219                         rebuild(false);
1220                     }
1221                     return;
1222                 }
1223             }
1224         }
1225 
1226         @Override
onLauncherInfoChanged()1227         public void onLauncherInfoChanged() {
1228             if (!mManageApplications.mShowSystem) {
1229                 rebuild(false);
1230             }
1231         }
1232 
1233         @Override
onAllSizesComputed()1234         public void onAllSizesComputed() {
1235             if (mLastSortMode == R.id.sort_order_size) {
1236                 rebuild(false);
1237             }
1238         }
1239 
onExtraViewCompleted()1240         public void onExtraViewCompleted() {
1241             int size = mActive.size();
1242             // If we have no elements, don't do anything.
1243             if (size < 1) {
1244                 return;
1245             }
1246             AppViewHolder holder = (AppViewHolder) mActive.get(size - 1).getTag();
1247 
1248             // HACK: The extra view has no AppEntry -- and should be the only element without one.
1249             // Thus, if the last active element has no AppEntry, it is the extra view.
1250             if (holder == null || holder.entry != null) {
1251                 return;
1252             }
1253 
1254             mExtraViewController.setupView(holder);
1255         }
1256 
getCount()1257         public int getCount() {
1258             if (mEntries == null) {
1259                 return 0;
1260             }
1261             int extraViewAddition =
1262                     (mExtraViewController != null && mExtraViewController.shouldShow()) ? 1 : 0;
1263             return mEntries.size() + extraViewAddition;
1264         }
1265 
getApplicationCount()1266         public int getApplicationCount() {
1267             return mEntries != null ? mEntries.size() : 0;
1268         }
1269 
getItem(int position)1270         public Object getItem(int position) {
1271             if (position == mEntries.size()) {
1272                 return mExtraViewController;
1273             }
1274             return mEntries.get(position);
1275         }
1276 
getAppEntry(int position)1277         public ApplicationsState.AppEntry getAppEntry(int position) {
1278             return mEntries.get(position);
1279         }
1280 
getItemId(int position)1281         public long getItemId(int position) {
1282             if (position == mEntries.size()) {
1283                 return -1;
1284             }
1285             return mEntries.get(position).id;
1286         }
1287 
1288         @Override
areAllItemsEnabled()1289         public boolean areAllItemsEnabled() {
1290             return false;
1291         }
1292 
1293         @Override
isEnabled(int position)1294         public boolean isEnabled(int position) {
1295             if (position == mEntries.size() && mExtraViewController != null &&
1296                     mExtraViewController.shouldShow()) {
1297                 return true;
1298             }
1299 
1300             if (mManageApplications.mListType != LIST_TYPE_HIGH_POWER) {
1301                 return true;
1302             }
1303             ApplicationsState.AppEntry entry = mEntries.get(position);
1304             return !PowerWhitelistBackend.getInstance().isSysWhitelisted(entry.info.packageName);
1305         }
1306 
getView(int position, View convertView, ViewGroup parent)1307         public View getView(int position, View convertView, ViewGroup parent) {
1308             // A ViewHolder keeps references to children views to avoid unnecessary calls
1309             // to findViewById() on each row.
1310             AppViewHolder holder = AppViewHolder.createOrRecycle(mManageApplications.mInflater,
1311                     convertView);
1312             convertView = holder.rootView;
1313 
1314             // Handle the extra view if it is the last entry.
1315             if (mEntries != null && mExtraViewController != null && position == mEntries.size()) {
1316                 mExtraViewController.setupView(holder);
1317                 convertView.setEnabled(true);
1318             } else {
1319                 // Bind the data efficiently with the holder
1320                 ApplicationsState.AppEntry entry = mEntries.get(position);
1321                 synchronized (entry) {
1322                     holder.entry = entry;
1323                     if (entry.label != null) {
1324                         holder.appName.setText(entry.label);
1325                     }
1326                     mState.ensureIcon(entry);
1327                     if (entry.icon != null) {
1328                         holder.appIcon.setImageDrawable(entry.icon);
1329                     }
1330                     updateSummary(holder);
1331                     updateDisableView(holder.disabled, entry.info);
1332                 }
1333                 convertView.setEnabled(isEnabled(position));
1334             }
1335 
1336             mActive.remove(convertView);
1337             mActive.add(convertView);
1338             return convertView;
1339         }
1340 
1341         @VisibleForTesting
updateDisableView(TextView view, ApplicationInfo info)1342         void updateDisableView(TextView view, ApplicationInfo info) {
1343             if ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
1344                 view.setVisibility(View.VISIBLE);
1345                 view.setText(R.string.not_installed);
1346             } else if (!info.enabled || info.enabledSetting
1347                     == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
1348                 view.setVisibility(View.VISIBLE);
1349                 view.setText(R.string.disabled);
1350             } else {
1351                 view.setVisibility(View.GONE);
1352             }
1353         }
1354 
updateSummary(AppViewHolder holder)1355         private void updateSummary(AppViewHolder holder) {
1356             switch (mManageApplications.mListType) {
1357                 case LIST_TYPE_NOTIFICATION:
1358                     if (holder.entry.extraInfo != null) {
1359                         holder.summary.setText(InstalledAppDetails.getNotificationSummary(
1360                                 (AppRow) holder.entry.extraInfo, mContext));
1361                     } else {
1362                         holder.summary.setText(null);
1363                     }
1364                     break;
1365 
1366                 case LIST_TYPE_USAGE_ACCESS:
1367                     if (holder.entry.extraInfo != null) {
1368                         holder.summary.setText((new UsageState((PermissionState) holder.entry
1369                                 .extraInfo)).isPermissible() ? R.string.switch_on_text :
1370                                 R.string.switch_off_text);
1371                     } else {
1372                         holder.summary.setText(null);
1373                     }
1374                     break;
1375 
1376                 case LIST_TYPE_HIGH_POWER:
1377                     holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry));
1378                     break;
1379 
1380                 case LIST_TYPE_OVERLAY:
1381                     holder.summary.setText(DrawOverlayDetails.getSummary(mContext, holder.entry));
1382                     break;
1383 
1384                 case LIST_TYPE_WRITE_SETTINGS:
1385                     holder.summary.setText(WriteSettingsDetails.getSummary(mContext,
1386                             holder.entry));
1387                     break;
1388 
1389                 case LIST_TYPE_MANAGE_SOURCES:
1390                     holder.summary.setText(ExternalSourcesDetails.getPreferenceSummary(mContext,
1391                             holder.entry));
1392                     break;
1393 
1394                 default:
1395                     holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
1396                     break;
1397             }
1398         }
1399 
1400         @Override
getFilter()1401         public Filter getFilter() {
1402             return mFilter;
1403         }
1404 
1405         @Override
onMovedToScrapHeap(View view)1406         public void onMovedToScrapHeap(View view) {
1407             mActive.remove(view);
1408         }
1409 
1410         @Override
getSections()1411         public Object[] getSections() {
1412             return mSections;
1413         }
1414 
1415         @Override
getPositionForSection(int sectionIndex)1416         public int getPositionForSection(int sectionIndex) {
1417             return mSections[sectionIndex].position;
1418         }
1419 
1420         @Override
getSectionForPosition(int position)1421         public int getSectionForPosition(int position) {
1422             return mPositionToSectionIndex[position];
1423         }
1424     }
1425 
1426     private static class SummaryProvider implements SummaryLoader.SummaryProvider {
1427 
1428         private final Context mContext;
1429         private final SummaryLoader mLoader;
1430         private ApplicationsState.Session mSession;
1431 
SummaryProvider(Context context, SummaryLoader loader)1432         private SummaryProvider(Context context, SummaryLoader loader) {
1433             mContext = context;
1434             mLoader = loader;
1435         }
1436 
1437         @Override
setListening(boolean listening)1438         public void setListening(boolean listening) {
1439             if (listening) {
1440                 new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
1441                         new PackageManagerWrapperImpl(mContext.getPackageManager())) {
1442                     @Override
1443                     protected void onCountComplete(int num) {
1444                         mLoader.setSummary(SummaryProvider.this,
1445                                 mContext.getString(R.string.apps_summary, num));
1446                     }
1447                 }.execute();
1448             }
1449         }
1450     }
1451 
1452     private static class SectionInfo {
1453         final String label;
1454         final int position;
1455 
SectionInfo(String label, int position)1456         public SectionInfo(String label, int position) {
1457             this.label = label;
1458             this.position = position;
1459         }
1460 
1461         @Override
toString()1462         public String toString() {
1463             return label;
1464         }
1465     }
1466 
1467     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
1468             = new SummaryLoader.SummaryProviderFactory() {
1469         @Override
1470         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
1471                                                                    SummaryLoader summaryLoader) {
1472             return new SummaryProvider(activity, summaryLoader);
1473         }
1474     };
1475 }
1476