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