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