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