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