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