ManageApplications.java revision a30e046597d46a28b6a36c62e6a0aee2b36dad83
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
17package com.android.settings.applications.manageapplications;
18
19import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
20import static com.android.settings.applications.manageapplications.AppFilterRegistry
21        .FILTER_APPS_ALL;
22import static com.android.settings.applications.manageapplications.AppFilterRegistry
23        .FILTER_APPS_DISABLED;
24import static com.android.settings.applications.manageapplications.AppFilterRegistry
25        .FILTER_APPS_ENABLED;
26import static com.android.settings.applications.manageapplications.AppFilterRegistry
27        .FILTER_APPS_FREQUENT;
28import static com.android.settings.applications.manageapplications.AppFilterRegistry
29        .FILTER_APPS_INSTANT;
30import static com.android.settings.applications.manageapplications.AppFilterRegistry
31        .FILTER_APPS_PERSONAL;
32import static com.android.settings.applications.manageapplications.AppFilterRegistry
33        .FILTER_APPS_POWER_WHITELIST;
34import static com.android.settings.applications.manageapplications.AppFilterRegistry
35        .FILTER_APPS_POWER_WHITELIST_ALL;
36import static com.android.settings.applications.manageapplications.AppFilterRegistry
37        .FILTER_APPS_RECENT;
38import static com.android.settings.applications.manageapplications.AppFilterRegistry
39        .FILTER_APPS_WORK;
40
41import android.annotation.Nullable;
42import android.annotation.StringRes;
43import android.app.Activity;
44import android.app.usage.IUsageStatsManager;
45import android.app.usage.UsageStatsManager;
46import android.content.Context;
47import android.content.Intent;
48import android.content.pm.ApplicationInfo;
49import android.content.pm.PackageItemInfo;
50import android.os.Bundle;
51import android.os.Environment;
52import android.os.ServiceManager;
53import android.os.UserHandle;
54import android.os.UserManager;
55import android.preference.PreferenceFrameLayout;
56import android.support.annotation.NonNull;
57import android.support.annotation.VisibleForTesting;
58import android.support.v7.widget.LinearLayoutManager;
59import android.support.v7.widget.RecyclerView;
60import android.text.TextUtils;
61import android.util.ArraySet;
62import android.util.Log;
63import android.view.LayoutInflater;
64import android.view.Menu;
65import android.view.MenuInflater;
66import android.view.MenuItem;
67import android.view.View;
68import android.view.ViewGroup;
69import android.widget.AdapterView;
70import android.widget.AdapterView.OnItemSelectedListener;
71import android.widget.ArrayAdapter;
72import android.widget.FrameLayout;
73import android.widget.Spinner;
74
75import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
76import com.android.settings.R;
77import com.android.settings.Settings;
78import com.android.settings.Settings.GamesStorageActivity;
79import com.android.settings.Settings.HighPowerApplicationsActivity;
80import com.android.settings.Settings.ManageExternalSourcesActivity;
81import com.android.settings.Settings.MoviesStorageActivity;
82import com.android.settings.Settings.OverlaySettingsActivity;
83import com.android.settings.Settings.StorageUseActivity;
84import com.android.settings.Settings.UsageAccessSettingsActivity;
85import com.android.settings.Settings.WriteSettingsActivity;
86import com.android.settings.SettingsActivity;
87import com.android.settings.applications.AppInfoBase;
88import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
89import com.android.settings.applications.AppStateBaseBridge;
90import com.android.settings.applications.AppStateDirectoryAccessBridge;
91import com.android.settings.applications.AppStateInstallAppsBridge;
92import com.android.settings.applications.AppStateNotificationBridge;
93import com.android.settings.applications.AppStateNotificationBridge.NotificationsSentState;
94import com.android.settings.applications.AppStateOverlayBridge;
95import com.android.settings.applications.AppStatePowerBridge;
96import com.android.settings.applications.AppStateUsageBridge;
97import com.android.settings.applications.AppStateUsageBridge.UsageState;
98import com.android.settings.applications.AppStateWriteSettingsBridge;
99import com.android.settings.applications.AppStorageSettings;
100import com.android.settings.applications.DefaultAppSettings;
101import com.android.settings.applications.DirectoryAccessDetails;
102import com.android.settings.applications.InstalledAppCounter;
103import com.android.settings.applications.UsageAccessDetails;
104import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
105import com.android.settings.applications.appinfo.DrawOverlayDetails;
106import com.android.settings.applications.appinfo.ExternalSourcesDetails;
107import com.android.settings.applications.appinfo.WriteSettingsDetails;
108import com.android.settings.core.InstrumentedFragment;
109import com.android.settings.core.SubSettingLauncher;
110import com.android.settings.dashboard.SummaryLoader;
111import com.android.settings.fuelgauge.HighPowerDetail;
112import com.android.settings.notification.AppNotificationSettings;
113import com.android.settings.notification.ConfigureNotificationSettings;
114import com.android.settings.notification.NotificationBackend;
115import com.android.settings.widget.LoadingViewController;
116import com.android.settings.wifi.AppStateChangeWifiStateBridge;
117import com.android.settings.wifi.ChangeWifiStateDetails;
118import com.android.settingslib.HelpUtils;
119import com.android.settingslib.applications.ApplicationsState;
120import com.android.settingslib.applications.ApplicationsState.AppEntry;
121import com.android.settingslib.applications.ApplicationsState.AppFilter;
122import com.android.settingslib.applications.ApplicationsState.CompoundFilter;
123import com.android.settingslib.applications.ApplicationsState.VolumeFilter;
124import com.android.settingslib.applications.StorageStatsSource;
125import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
126import com.android.settingslib.utils.ThreadUtils;
127import com.android.settingslib.wrapper.PackageManagerWrapper;
128
129import java.util.ArrayList;
130import java.util.Arrays;
131import java.util.Collections;
132import java.util.Comparator;
133import 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 */
141public 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(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().isSysWhitelisted(entry.info.packageName);
1280        }
1281
1282        @Override
1283        public void onBindViewHolder(ApplicationViewHolder holder, int position) {
1284            if (mEntries != null && mExtraViewController != null && position == mEntries.size()) {
1285                // set up view for extra view controller
1286                mExtraViewController.setupView(holder);
1287            } else {
1288                // Bind the data efficiently with the holder
1289                ApplicationsState.AppEntry entry = mEntries.get(position);
1290                synchronized (entry) {
1291                    holder.setTitle(entry.label);
1292                    mState.ensureIcon(entry);
1293                    holder.setIcon(entry.icon);
1294                    updateSummary(holder, entry);
1295                    updateSwitch(holder, entry);
1296                    holder.updateDisableView(entry.info);
1297                }
1298                holder.setEnabled(isEnabled(position));
1299            }
1300            holder.itemView.setOnClickListener(mManageApplications);
1301        }
1302
1303        private void updateSummary(ApplicationViewHolder holder, AppEntry entry) {
1304            switch (mManageApplications.mListType) {
1305                case LIST_TYPE_NOTIFICATION:
1306                    if (entry.extraInfo != null) {
1307                        holder.setSummary(AppStateNotificationBridge.getSummary(mContext,
1308                                (NotificationsSentState) entry.extraInfo,
1309                                (mLastSortMode == R.id.sort_order_recent_notification)));
1310                    } else {
1311                        holder.setSummary(null);
1312                    }
1313                    break;
1314                case LIST_TYPE_USAGE_ACCESS:
1315                    if (entry.extraInfo != null) {
1316                        holder.setSummary(
1317                                (new UsageState((PermissionState) entry.extraInfo)).isPermissible()
1318                                        ? R.string.app_permission_summary_allowed
1319                                        : R.string.app_permission_summary_not_allowed);
1320                    } else {
1321                        holder.setSummary(null);
1322                    }
1323                    break;
1324                case LIST_TYPE_HIGH_POWER:
1325                    holder.setSummary(HighPowerDetail.getSummary(mContext, entry));
1326                    break;
1327                case LIST_TYPE_OVERLAY:
1328                    holder.setSummary(DrawOverlayDetails.getSummary(mContext, entry));
1329                    break;
1330                case LIST_TYPE_WRITE_SETTINGS:
1331                    holder.setSummary(WriteSettingsDetails.getSummary(mContext, entry));
1332                    break;
1333                case LIST_TYPE_MANAGE_SOURCES:
1334                    holder.setSummary(ExternalSourcesDetails.getPreferenceSummary(mContext, entry));
1335                    break;
1336                case LIST_TYPE_DIRECTORY_ACCESS:
1337                    holder.setSummary(null);
1338                    break;
1339                case LIST_TYPE_WIFI_ACCESS:
1340                    holder.setSummary(ChangeWifiStateDetails.getSummary(mContext, entry));
1341                    break;
1342                default:
1343                    holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize);
1344                    break;
1345            }
1346        }
1347
1348        private void updateSwitch(ApplicationViewHolder holder, AppEntry entry) {
1349            switch (mManageApplications.mListType) {
1350                case LIST_TYPE_NOTIFICATION:
1351                    holder.updateSwitch(((AppStateNotificationBridge) mExtraInfoBridge)
1352                                    .getSwitchOnClickListener(entry),
1353                            AppStateNotificationBridge.enableSwitch(entry),
1354                            AppStateNotificationBridge.checkSwitch(entry));
1355                    if (entry.extraInfo != null) {
1356                        holder.setSummary(AppStateNotificationBridge.getSummary(mContext,
1357                                (NotificationsSentState) entry.extraInfo,
1358                                (mLastSortMode == R.id.sort_order_recent_notification)));
1359                    } else {
1360                        holder.setSummary(null);
1361                    }
1362                    break;
1363            }
1364        }
1365
1366        private boolean hasExtraView() {
1367            return mExtraViewController != null
1368                    && mExtraViewController.shouldShow();
1369        }
1370
1371        public static class OnScrollListener extends RecyclerView.OnScrollListener {
1372            private int mScrollState = SCROLL_STATE_IDLE;
1373            private boolean mDelayNotifyDataChange;
1374            private ApplicationsAdapter mAdapter;
1375
1376            public OnScrollListener(ApplicationsAdapter adapter) {
1377                mAdapter = adapter;
1378            }
1379
1380            @Override
1381            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
1382                mScrollState = newState;
1383                if (mScrollState == SCROLL_STATE_IDLE && mDelayNotifyDataChange) {
1384                    mDelayNotifyDataChange = false;
1385                    mAdapter.notifyDataSetChanged();
1386                }
1387            }
1388
1389            public void postNotifyItemChange(int index) {
1390                if (mScrollState == SCROLL_STATE_IDLE) {
1391                    mAdapter.notifyItemChanged(index);
1392                } else {
1393                    mDelayNotifyDataChange = true;
1394                }
1395            }
1396        }
1397    }
1398
1399    private static class SummaryProvider implements SummaryLoader.SummaryProvider {
1400
1401        private final Context mContext;
1402        private final SummaryLoader mLoader;
1403
1404        private SummaryProvider(Context context, SummaryLoader loader) {
1405            mContext = context;
1406            mLoader = loader;
1407        }
1408
1409        @Override
1410        public void setListening(boolean listening) {
1411            if (listening) {
1412                new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
1413                        new PackageManagerWrapper(mContext.getPackageManager())) {
1414                    @Override
1415                    protected void onCountComplete(int num) {
1416                        mLoader.setSummary(SummaryProvider.this,
1417                                mContext.getString(R.string.apps_summary, num));
1418                    }
1419                }.execute();
1420            }
1421        }
1422    }
1423
1424    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
1425            = new SummaryLoader.SummaryProviderFactory() {
1426        @Override
1427        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
1428                SummaryLoader summaryLoader) {
1429            return new SummaryProvider(activity, summaryLoader);
1430        }
1431    };
1432}
1433