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