Home | History | Annotate | Download | only in applications
      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
    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
    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 
    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
    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 
    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 
    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
    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
    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
    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
    530     public void onPause() {
    531         super.onPause();
    532         if (mApplications != null) {
    533             mApplications.pause();
    534         }
    535     }
    536 
    537     @Override
    538     public void onStop() {
    539         super.onStop();
    540         mResetAppsHelper.stop();
    541     }
    542 
    543     @Override
    544     public void onDestroyView() {
    545         super.onDestroyView();
    546 
    547         if (mApplications != null) {
    548             mApplications.release();
    549         }
    550         mRootView = null;
    551     }
    552 
    553     @Override
    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
    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 
    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
    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
    627     public void onPrepareOptionsMenu(Menu menu) {
    628         updateOptionsMenu();
    629     }
    630 
    631     @Override
    632     public void onDestroyOptionsMenu() {
    633         mOptionsMenu = null;
    634     }
    635 
    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
    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
    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
    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
    717     public void onNothingSelected(AdapterView<?> parent) {
    718     }
    719 
    720     public void updateView() {
    721         updateOptionsMenu();
    722         final Activity host = getActivity();
    723         if (host != null) {
    724             host.invalidateOptionsMenu();
    725         }
    726     }
    727 
    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 
    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 
    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 
    756         public int getFilter(int position) {
    757             return mFilterOptions.get(position);
    758         }
    759 
    760         public void setFilterEnabled(int filter, boolean enabled) {
    761             if (enabled) {
    762                 enableFilter(filter);
    763             } else {
    764                 disableFilter(filter);
    765             }
    766         }
    767 
    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 
    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
    802         public int getCount() {
    803             return mFilterOptions.size();
    804         }
    805 
    806         @Override
    807         public CharSequence getItem(int position) {
    808             return getFilterString(mFilterOptions.get(position));
    809         }
    810 
    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 
    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 
    912         public void setCompositeFilter(AppFilter compositeFilter) {
    913             mCompositeFilter = compositeFilter;
    914             rebuild(true);
    915         }
    916 
    917         public void setFilter(int filter) {
    918             mFilterMode = filter;
    919             rebuild(true);
    920         }
    921 
    922         public void setExtraViewController(FileViewHolderController extraViewController) {
    923             mExtraViewController = extraViewController;
    924             mBgHandler.post(() -> {
    925                 mExtraViewController.queryStats();
    926                 mFgHandler.post(() -> {
    927                     onExtraViewCompleted();
    928                 });
    929             });
    930         }
    931 
    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 
    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 
    961         public void release() {
    962             mSession.release();
    963             if (mExtraInfoBridge != null) {
    964                 mExtraInfoBridge.release();
    965             }
    966         }
    967 
    968         public void rebuild(int sort) {
    969             if (sort == mLastSortMode) {
    970                 return;
    971             }
    972             mLastSortMode = sort;
    973             rebuild(true);
    974         }
    975 
    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 
   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 
   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
   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 
   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 
   1143         private void updateLoading() {
   1144             Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
   1145                     mManageApplications.mListContainer,
   1146                     mHasReceivedLoadEntries && mSession.getAllApps().size() != 0, false);
   1147         }
   1148 
   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
   1170         public void onExtraInfoUpdated() {
   1171             mHasReceivedBridgeCallback = true;
   1172             rebuild(false);
   1173         }
   1174 
   1175         @Override
   1176         public void onRunningStateChanged(boolean running) {
   1177             mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running);
   1178         }
   1179 
   1180         @Override
   1181         public void onPackageListChanged() {
   1182             rebuild(false);
   1183         }
   1184 
   1185         @Override
   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
   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
   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
   1227         public void onLauncherInfoChanged() {
   1228             if (!mManageApplications.mShowSystem) {
   1229                 rebuild(false);
   1230             }
   1231         }
   1232 
   1233         @Override
   1234         public void onAllSizesComputed() {
   1235             if (mLastSortMode == R.id.sort_order_size) {
   1236                 rebuild(false);
   1237             }
   1238         }
   1239 
   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 
   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 
   1266         public int getApplicationCount() {
   1267             return mEntries != null ? mEntries.size() : 0;
   1268         }
   1269 
   1270         public Object getItem(int position) {
   1271             if (position == mEntries.size()) {
   1272                 return mExtraViewController;
   1273             }
   1274             return mEntries.get(position);
   1275         }
   1276 
   1277         public ApplicationsState.AppEntry getAppEntry(int position) {
   1278             return mEntries.get(position);
   1279         }
   1280 
   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
   1289         public boolean areAllItemsEnabled() {
   1290             return false;
   1291         }
   1292 
   1293         @Override
   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 
   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
   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 
   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
   1401         public Filter getFilter() {
   1402             return mFilter;
   1403         }
   1404 
   1405         @Override
   1406         public void onMovedToScrapHeap(View view) {
   1407             mActive.remove(view);
   1408         }
   1409 
   1410         @Override
   1411         public Object[] getSections() {
   1412             return mSections;
   1413         }
   1414 
   1415         @Override
   1416         public int getPositionForSection(int sectionIndex) {
   1417             return mSections[sectionIndex].position;
   1418         }
   1419 
   1420         @Override
   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 
   1432         private SummaryProvider(Context context, SummaryLoader loader) {
   1433             mContext = context;
   1434             mLoader = loader;
   1435         }
   1436 
   1437         @Override
   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 
   1456         public SectionInfo(String label, int position) {
   1457             this.label = label;
   1458             this.position = position;
   1459         }
   1460 
   1461         @Override
   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