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;
18
19import android.app.Activity;
20import android.content.Context;
21import android.content.Intent;
22import android.content.pm.ApplicationInfo;
23import android.content.pm.PackageItemInfo;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.icu.text.AlphabeticIndex;
27import android.os.Bundle;
28import android.os.Environment;
29import android.os.Handler;
30import android.os.LocaleList;
31import android.os.UserHandle;
32import android.os.UserManager;
33import android.preference.PreferenceFrameLayout;
34import android.text.TextUtils;
35import android.util.ArraySet;
36import android.util.Log;
37import android.view.LayoutInflater;
38import android.view.Menu;
39import android.view.MenuInflater;
40import android.view.MenuItem;
41import android.view.View;
42import android.view.ViewGroup;
43import android.widget.AbsListView;
44import android.widget.AdapterView;
45import android.widget.AdapterView.OnItemClickListener;
46import android.widget.AdapterView.OnItemSelectedListener;
47import android.widget.ArrayAdapter;
48import android.widget.BaseAdapter;
49import android.widget.Filter;
50import android.widget.Filterable;
51import android.widget.FrameLayout;
52import android.widget.ListView;
53import android.widget.SectionIndexer;
54import android.widget.Spinner;
55import com.android.internal.logging.MetricsProto.MetricsEvent;
56import com.android.settings.AppHeader;
57import com.android.settings.InstrumentedFragment;
58import com.android.settings.R;
59import com.android.settings.Settings.AllApplicationsActivity;
60import com.android.settings.Settings.HighPowerApplicationsActivity;
61import com.android.settings.Settings.NotificationAppListActivity;
62import com.android.settings.Settings.OverlaySettingsActivity;
63import com.android.settings.Settings.StorageUseActivity;
64import com.android.settings.Settings.UsageAccessSettingsActivity;
65import com.android.settings.Settings.WriteSettingsActivity;
66import com.android.settings.SettingsActivity;
67import com.android.settings.Utils;
68import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
69import com.android.settings.applications.AppStateUsageBridge.UsageState;
70import com.android.settings.dashboard.SummaryLoader;
71import com.android.settings.fuelgauge.HighPowerDetail;
72import com.android.settings.fuelgauge.PowerWhitelistBackend;
73import com.android.settings.notification.AppNotificationSettings;
74import com.android.settings.notification.ConfigureNotificationSettings;
75import com.android.settings.notification.NotificationBackend;
76import com.android.settings.notification.NotificationBackend.AppRow;
77import com.android.settingslib.HelpUtils;
78import com.android.settingslib.applications.ApplicationsState;
79import com.android.settingslib.applications.ApplicationsState.AppEntry;
80import com.android.settingslib.applications.ApplicationsState.AppFilter;
81import com.android.settingslib.applications.ApplicationsState.CompoundFilter;
82import com.android.settingslib.applications.ApplicationsState.VolumeFilter;
83
84import java.util.ArrayList;
85import java.util.Collections;
86import java.util.Comparator;
87import java.util.List;
88import java.util.Locale;
89
90/**
91 * Activity to pick an application that will be used to display installation information and
92 * options to uninstall/delete user data for system applications. This activity
93 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
94 * intent.
95 */
96public class ManageApplications extends InstrumentedFragment
97        implements OnItemClickListener, OnItemSelectedListener {
98
99    static final String TAG = "ManageApplications";
100    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
101
102    // Intent extras.
103    public static final String EXTRA_CLASSNAME = "classname";
104    // Used for storage only.
105    public static final String EXTRA_VOLUME_UUID = "volumeUuid";
106    public static final String EXTRA_VOLUME_NAME = "volumeName";
107
108    private static final String EXTRA_SORT_ORDER = "sortOrder";
109    private static final String EXTRA_SHOW_SYSTEM = "showSystem";
110    private static final String EXTRA_HAS_ENTRIES = "hasEntries";
111    private static final String EXTRA_HAS_BRIDGE = "hasBridge";
112
113    // attributes used as keys when passing values to InstalledAppDetails activity
114    public static final String APP_CHG = "chg";
115
116    // constant value that can be used to check return code from sub activity.
117    private static final int INSTALLED_APP_DETAILS = 1;
118    private static final int ADVANCED_SETTINGS = 2;
119
120    public static final int SIZE_TOTAL = 0;
121    public static final int SIZE_INTERNAL = 1;
122    public static final int SIZE_EXTERNAL = 2;
123
124    // Filter options used for displayed list of applications
125    // The order which they appear is the order they will show when spinner is present.
126    public static final int FILTER_APPS_POWER_WHITELIST = 0;
127    public static final int FILTER_APPS_POWER_WHITELIST_ALL = 1;
128    public static final int FILTER_APPS_ALL = 2;
129    public static final int FILTER_APPS_ENABLED = 3;
130    public static final int FILTER_APPS_DISABLED = 4;
131    public static final int FILTER_APPS_BLOCKED = 5;
132    public static final int FILTER_APPS_SILENT = 6;
133    public static final int FILTER_APPS_SENSITIVE = 7;
134    public static final int FILTER_APPS_HIDE_NOTIFICATIONS = 8;
135    public static final int FILTER_APPS_PRIORITY = 9;
136    public static final int FILTER_APPS_PERSONAL = 10;
137    public static final int FILTER_APPS_WORK = 11;
138    public static final int FILTER_APPS_USAGE_ACCESS = 13;
139    public static final int FILTER_APPS_WITH_OVERLAY = 14;
140    public static final int FILTER_APPS_WRITE_SETTINGS = 15;
141
142    // This is the string labels for the filter modes above, the order must be kept in sync.
143    public static final int[] FILTER_LABELS = new int[]{
144            R.string.high_power_filter_on, // High power whitelist, on
145            R.string.filter_all_apps,      // Without disabled until used
146            R.string.filter_all_apps,      // All apps
147            R.string.filter_enabled_apps,  // Enabled
148            R.string.filter_apps_disabled, // Disabled
149            R.string.filter_notif_blocked_apps,   // Blocked Notifications
150            R.string.filter_notif_silent,    // Silenced Notifications
151            R.string.filter_notif_sensitive_apps, // Sensitive Notifications
152            R.string.filter_notif_hide_notifications_apps, // Sensitive Notifications
153            R.string.filter_notif_priority_apps,  // Priority Notifications
154            R.string.filter_personal_apps, // Personal
155            R.string.filter_work_apps,     // Work
156            R.string.filter_with_domain_urls_apps,     // Domain URLs
157            R.string.filter_all_apps,      // Usage access screen, never displayed
158            R.string.filter_overlay_apps,   // Apps with overlay permission
159            R.string.filter_write_settings_apps,   // Apps that can write system settings
160    };
161    // This is the actual mapping to filters from FILTER_ constants above, the order must
162    // be kept in sync.
163    public static final AppFilter[] FILTERS = new AppFilter[]{
164            new CompoundFilter(AppStatePowerBridge.FILTER_POWER_WHITELISTED,
165                    ApplicationsState.FILTER_ALL_ENABLED),     // High power whitelist, on
166            new CompoundFilter(ApplicationsState.FILTER_WITHOUT_DISABLED_UNTIL_USED,
167                    ApplicationsState.FILTER_ALL_ENABLED),     // Without disabled until used
168            ApplicationsState.FILTER_EVERYTHING,  // All apps
169            ApplicationsState.FILTER_ALL_ENABLED, // Enabled
170            ApplicationsState.FILTER_DISABLED,    // Disabled
171            AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED,   // Blocked Notifications
172            AppStateNotificationBridge.FILTER_APP_NOTIFICATION_SILENCED,   // Silenced Notifications
173            AppStateNotificationBridge.FILTER_APP_NOTIFICATION_HIDE_SENSITIVE, // Sensitive Notifications
174            AppStateNotificationBridge.FILTER_APP_NOTIFICATION_HIDE_ALL, // Hide all Notifications
175            AppStateNotificationBridge.FILTER_APP_NOTIFICATION_PRIORITY,  // Priority Notifications
176            ApplicationsState.FILTER_PERSONAL,    // Personal
177            ApplicationsState.FILTER_WORK,        // Work
178            ApplicationsState.FILTER_WITH_DOMAIN_URLS,   // Apps with Domain URLs
179            AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
180            AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW,   // Apps that can draw overlays
181            AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS,  // Apps that can write system settings
182    };
183
184    // sort order
185    private int mSortOrder = R.id.sort_order_alpha;
186
187    // whether showing system apps.
188    private boolean mShowSystem;
189
190    private ApplicationsState mApplicationsState;
191
192    public int mListType;
193    public int mFilter;
194
195    public ApplicationsAdapter mApplications;
196
197    private View mLoadingContainer;
198
199    private View mListContainer;
200
201    // ListView used to display list
202    private ListView mListView;
203
204    // Size resource used for packages whose size computation failed for some reason
205    CharSequence mInvalidSizeStr;
206
207    // layout inflater object used to inflate views
208    private LayoutInflater mInflater;
209
210    private String mCurrentPkgName;
211    private int mCurrentUid;
212    private boolean mFinishAfterDialog;
213
214    private Menu mOptionsMenu;
215
216    public static final int LIST_TYPE_MAIN = 0;
217    public static final int LIST_TYPE_NOTIFICATION = 1;
218    public static final int LIST_TYPE_STORAGE = 3;
219    public static final int LIST_TYPE_USAGE_ACCESS = 4;
220    public static final int LIST_TYPE_HIGH_POWER = 5;
221    public static final int LIST_TYPE_OVERLAY = 6;
222    public static final int LIST_TYPE_WRITE_SETTINGS = 7;
223
224    private View mRootView;
225
226    private View mSpinnerHeader;
227    private Spinner mFilterSpinner;
228    private FilterSpinnerAdapter mFilterAdapter;
229    private NotificationBackend mNotifBackend;
230    private ResetAppsHelper mResetAppsHelper;
231    private String mVolumeUuid;
232    private String mVolumeName;
233
234    @Override
235    public void onCreate(Bundle savedInstanceState) {
236        super.onCreate(savedInstanceState);
237        setHasOptionsMenu(true);
238        mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
239
240        Intent intent = getActivity().getIntent();
241        Bundle args = getArguments();
242        String className = args != null ? args.getString(EXTRA_CLASSNAME) : null;
243        if (className == null) {
244            className = intent.getComponent().getClassName();
245        }
246        if (className.equals(AllApplicationsActivity.class.getName())) {
247            mShowSystem = true;
248        } else if (className.equals(NotificationAppListActivity.class.getName())) {
249            mListType = LIST_TYPE_NOTIFICATION;
250            mNotifBackend = new NotificationBackend();
251        } else if (className.equals(StorageUseActivity.class.getName())) {
252            if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) {
253                mVolumeUuid = args.getString(EXTRA_VOLUME_UUID);
254                mVolumeName = args.getString(EXTRA_VOLUME_NAME);
255                mListType = LIST_TYPE_STORAGE;
256            } else {
257                // No volume selected, display a normal list, sorted by size.
258                mListType = LIST_TYPE_MAIN;
259            }
260            mSortOrder = R.id.sort_order_size;
261        } else if (className.equals(UsageAccessSettingsActivity.class.getName())) {
262            mListType = LIST_TYPE_USAGE_ACCESS;
263        } else if (className.equals(HighPowerApplicationsActivity.class.getName())) {
264            mListType = LIST_TYPE_HIGH_POWER;
265            // Default to showing system.
266            mShowSystem = true;
267        } else if (className.equals(OverlaySettingsActivity.class.getName())) {
268            mListType = LIST_TYPE_OVERLAY;
269        } else if (className.equals(WriteSettingsActivity.class.getName())) {
270            mListType = LIST_TYPE_WRITE_SETTINGS;
271        } else {
272            mListType = LIST_TYPE_MAIN;
273        }
274        mFilter = getDefaultFilter();
275
276        if (savedInstanceState != null) {
277            mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
278            mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
279        }
280
281        mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
282
283        mResetAppsHelper = new ResetAppsHelper(getActivity());
284    }
285
286
287    @Override
288    public View onCreateView(LayoutInflater inflater, ViewGroup container,
289                             Bundle savedInstanceState) {
290        // initialize the inflater
291        mInflater = inflater;
292
293        mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
294        mLoadingContainer = mRootView.findViewById(R.id.loading_container);
295        mLoadingContainer.setVisibility(View.VISIBLE);
296        mListContainer = mRootView.findViewById(R.id.list_container);
297        if (mListContainer != null) {
298            // Create adapter and list view here
299            View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
300            ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
301            if (emptyView != null) {
302                lv.setEmptyView(emptyView);
303            }
304            lv.setOnItemClickListener(this);
305            lv.setSaveEnabled(true);
306            lv.setItemsCanFocus(true);
307            lv.setTextFilterEnabled(true);
308            mListView = lv;
309            mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
310            if (savedInstanceState != null) {
311                mApplications.mHasReceivedLoadEntries =
312                        savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false);
313                mApplications.mHasReceivedBridgeCallback =
314                        savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false);
315            }
316            mListView.setAdapter(mApplications);
317            mListView.setRecyclerListener(mApplications);
318            mListView.setFastScrollEnabled(isFastScrollEnabled());
319
320            Utils.prepareCustomPreferencesList(container, mRootView, mListView, false);
321        }
322
323        // We have to do this now because PreferenceFrameLayout looks at it
324        // only when the view is added.
325        if (container instanceof PreferenceFrameLayout) {
326            ((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true;
327        }
328
329        createHeader();
330
331        mResetAppsHelper.onRestoreInstanceState(savedInstanceState);
332
333        return mRootView;
334    }
335
336    private void createHeader() {
337        Activity activity = getActivity();
338        FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
339        mSpinnerHeader = (ViewGroup) activity.getLayoutInflater()
340                .inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
341        mFilterSpinner = (Spinner) mSpinnerHeader.findViewById(R.id.filter_spinner);
342        mFilterAdapter = new FilterSpinnerAdapter(this);
343        mFilterSpinner.setAdapter(mFilterAdapter);
344        mFilterSpinner.setOnItemSelectedListener(this);
345        pinnedHeader.addView(mSpinnerHeader, 0);
346
347        mFilterAdapter.enableFilter(getDefaultFilter());
348        if (mListType == LIST_TYPE_MAIN) {
349            if (UserManager.get(getActivity()).getUserProfiles().size() > 1) {
350                mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL);
351                mFilterAdapter.enableFilter(FILTER_APPS_WORK);
352            }
353        }
354        if (mListType == LIST_TYPE_NOTIFICATION) {
355            mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED);
356            mFilterAdapter.enableFilter(FILTER_APPS_SILENT);
357            mFilterAdapter.enableFilter(FILTER_APPS_SENSITIVE);
358            mFilterAdapter.enableFilter(FILTER_APPS_HIDE_NOTIFICATIONS);
359            mFilterAdapter.enableFilter(FILTER_APPS_PRIORITY);
360        }
361        if (mListType == LIST_TYPE_HIGH_POWER) {
362            mFilterAdapter.enableFilter(FILTER_APPS_POWER_WHITELIST_ALL);
363        }
364        if (mListType == LIST_TYPE_STORAGE) {
365            mApplications.setOverrideFilter(new VolumeFilter(mVolumeUuid));
366        }
367    }
368
369    @Override
370    public void onViewCreated(View view, Bundle savedInstanceState) {
371        super.onViewCreated(view, savedInstanceState);
372        if (mListType == LIST_TYPE_STORAGE) {
373            FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
374            AppHeader.createAppHeader(getActivity(), null, mVolumeName, null, -1, pinnedHeader);
375        }
376    }
377
378    private int getDefaultFilter() {
379        switch (mListType) {
380            case LIST_TYPE_USAGE_ACCESS:
381                return FILTER_APPS_USAGE_ACCESS;
382            case LIST_TYPE_HIGH_POWER:
383                return FILTER_APPS_POWER_WHITELIST;
384            case LIST_TYPE_OVERLAY:
385                return FILTER_APPS_WITH_OVERLAY;
386            case LIST_TYPE_WRITE_SETTINGS:
387                return FILTER_APPS_WRITE_SETTINGS;
388            default:
389                return FILTER_APPS_ALL;
390        }
391    }
392
393    private boolean isFastScrollEnabled() {
394        switch (mListType) {
395            case LIST_TYPE_MAIN:
396            case LIST_TYPE_NOTIFICATION:
397            case LIST_TYPE_STORAGE:
398                return mSortOrder == R.id.sort_order_alpha;
399            default:
400                return false;
401        }
402    }
403
404    @Override
405    protected int getMetricsCategory() {
406        switch (mListType) {
407            case LIST_TYPE_MAIN:
408                return MetricsEvent.MANAGE_APPLICATIONS;
409            case LIST_TYPE_NOTIFICATION:
410                return MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS;
411            case LIST_TYPE_STORAGE:
412                return MetricsEvent.APPLICATIONS_STORAGE_APPS;
413            case LIST_TYPE_USAGE_ACCESS:
414                return MetricsEvent.USAGE_ACCESS;
415            case LIST_TYPE_HIGH_POWER:
416                return MetricsEvent.APPLICATIONS_HIGH_POWER_APPS;
417            case LIST_TYPE_OVERLAY:
418                return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
419            case LIST_TYPE_WRITE_SETTINGS:
420                return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
421            default:
422                return MetricsEvent.VIEW_UNKNOWN;
423        }
424    }
425
426    @Override
427    public void onResume() {
428        super.onResume();
429        updateView();
430        updateOptionsMenu();
431        if (mApplications != null) {
432            mApplications.resume(mSortOrder);
433            mApplications.updateLoading();
434        }
435    }
436
437    @Override
438    public void onSaveInstanceState(Bundle outState) {
439        super.onSaveInstanceState(outState);
440        mResetAppsHelper.onSaveInstanceState(outState);
441        outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
442        outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
443        outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries);
444        outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback);
445    }
446
447    @Override
448    public void onPause() {
449        super.onPause();
450        if (mApplications != null) {
451            mApplications.pause();
452        }
453    }
454
455    @Override
456    public void onStop() {
457        super.onStop();
458        mResetAppsHelper.stop();
459    }
460
461    @Override
462    public void onDestroyView() {
463        super.onDestroyView();
464
465        if (mApplications != null) {
466            mApplications.release();
467        }
468        mRootView = null;
469    }
470
471    @Override
472    public void onActivityResult(int requestCode, int resultCode, Intent data) {
473        if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
474            if (mListType == LIST_TYPE_NOTIFICATION) {
475                mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
476            } else if (mListType == LIST_TYPE_HIGH_POWER || mListType == LIST_TYPE_OVERLAY
477                    || mListType == LIST_TYPE_WRITE_SETTINGS) {
478                if (mFinishAfterDialog) {
479                    getActivity().onBackPressed();
480                } else {
481                    mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
482                }
483            } else {
484                mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid));
485            }
486        }
487    }
488
489    // utility method used to start sub activity
490    private void startApplicationDetailsActivity() {
491        switch (mListType) {
492            case LIST_TYPE_NOTIFICATION:
493                startAppInfoFragment(AppNotificationSettings.class,
494                        R.string.app_notifications_title);
495                break;
496            case LIST_TYPE_USAGE_ACCESS:
497                startAppInfoFragment(UsageAccessDetails.class, R.string.usage_access);
498                break;
499            case LIST_TYPE_STORAGE:
500                startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings);
501                break;
502            case LIST_TYPE_HIGH_POWER:
503                HighPowerDetail.show(this, mCurrentPkgName, INSTALLED_APP_DETAILS,
504                        mFinishAfterDialog);
505                break;
506            case LIST_TYPE_OVERLAY:
507                startAppInfoFragment(DrawOverlayDetails.class, R.string.overlay_settings);
508                break;
509            case LIST_TYPE_WRITE_SETTINGS:
510                startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings);
511                break;
512            // TODO: Figure out if there is a way where we can spin up the profile's settings
513            // process ahead of time, to avoid a long load of data when user clicks on a managed app.
514            // Maybe when they load the list of apps that contains managed profile apps.
515            default:
516                startAppInfoFragment(InstalledAppDetails.class, R.string.application_info_label);
517                break;
518        }
519    }
520
521    private void startAppInfoFragment(Class<?> fragment, int titleRes) {
522        AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this,
523                INSTALLED_APP_DETAILS);
524    }
525
526    @Override
527    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
528        HelpUtils.prepareHelpMenuItem(getActivity(), menu, mListType == LIST_TYPE_MAIN
529                ? R.string.help_uri_apps : R.string.help_uri_notifications, getClass().getName());
530        mOptionsMenu = menu;
531        inflater.inflate(R.menu.manage_apps, menu);
532        updateOptionsMenu();
533    }
534
535    @Override
536    public void onPrepareOptionsMenu(Menu menu) {
537        updateOptionsMenu();
538    }
539
540    @Override
541    public void onDestroyOptionsMenu() {
542        mOptionsMenu = null;
543    }
544
545    void updateOptionsMenu() {
546        if (mOptionsMenu == null) {
547            return;
548        }
549        mOptionsMenu.findItem(R.id.advanced).setVisible(
550                mListType == LIST_TYPE_MAIN || mListType == LIST_TYPE_NOTIFICATION);
551
552        mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE
553                && mSortOrder != R.id.sort_order_alpha);
554        mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE
555                && mSortOrder != R.id.sort_order_size);
556
557        mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem
558                && mListType != LIST_TYPE_HIGH_POWER);
559        mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem
560                && mListType != LIST_TYPE_HIGH_POWER);
561    }
562
563    @Override
564    public boolean onOptionsItemSelected(MenuItem item) {
565        int menuId = item.getItemId();
566        switch (item.getItemId()) {
567            case R.id.sort_order_alpha:
568            case R.id.sort_order_size:
569                mSortOrder = menuId;
570                mListView.setFastScrollEnabled(isFastScrollEnabled());
571                if (mApplications != null) {
572                    mApplications.rebuild(mSortOrder);
573                }
574                break;
575            case R.id.show_system:
576            case R.id.hide_system:
577                mShowSystem = !mShowSystem;
578                mApplications.rebuild(false);
579                break;
580            case R.id.reset_app_preferences:
581                mResetAppsHelper.buildResetDialog();
582                return true;
583            case R.id.advanced:
584                if (mListType == LIST_TYPE_NOTIFICATION) {
585                    ((SettingsActivity) getActivity()).startPreferencePanel(
586                            ConfigureNotificationSettings.class.getName(), null,
587                            R.string.configure_notification_settings, null, this, ADVANCED_SETTINGS);
588                } else {
589                    ((SettingsActivity) getActivity()).startPreferencePanel(
590                            AdvancedAppSettings.class.getName(), null, R.string.configure_apps,
591                            null, this, ADVANCED_SETTINGS);
592                }
593                return true;
594            default:
595                // Handle the home button
596                return false;
597        }
598        updateOptionsMenu();
599        return true;
600    }
601
602    @Override
603    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
604        if (mApplications != null && mApplications.getCount() > position) {
605            ApplicationsState.AppEntry entry = mApplications.getAppEntry(position);
606            mCurrentPkgName = entry.info.packageName;
607            mCurrentUid = entry.info.uid;
608            startApplicationDetailsActivity();
609        }
610    }
611
612    @Override
613    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
614        mFilter = mFilterAdapter.getFilter(position);
615        mApplications.setFilter(mFilter);
616        if (DEBUG) Log.d(TAG, "Selecting filter " + mFilter);
617    }
618
619    @Override
620    public void onNothingSelected(AdapterView<?> parent) {
621    }
622
623    public void updateView() {
624        updateOptionsMenu();
625        final Activity host = getActivity();
626        if (host != null) {
627            host.invalidateOptionsMenu();
628        }
629    }
630
631    public void setHasDisabled(boolean hasDisabledApps) {
632        if (mListType != LIST_TYPE_MAIN) {
633            return;
634        }
635        mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps);
636        mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps);
637    }
638
639    static class FilterSpinnerAdapter extends ArrayAdapter<CharSequence> {
640
641        private final ManageApplications mManageApplications;
642
643        // Use ArrayAdapter for view logic, but have our own list for managing
644        // the options available.
645        private final ArrayList<Integer> mFilterOptions = new ArrayList<>();
646
647        public FilterSpinnerAdapter(ManageApplications manageApplications) {
648            super(manageApplications.getActivity(), R.layout.filter_spinner_item);
649            setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
650            mManageApplications = manageApplications;
651        }
652
653        public int getFilter(int position) {
654            return mFilterOptions.get(position);
655        }
656
657        public void setFilterEnabled(int filter, boolean enabled) {
658            if (enabled) {
659                enableFilter(filter);
660            } else {
661                disableFilter(filter);
662            }
663        }
664
665        public void enableFilter(int filter) {
666            if (mFilterOptions.contains(filter)) return;
667            if (DEBUG) Log.d(TAG, "Enabling filter " + filter);
668            mFilterOptions.add(filter);
669            Collections.sort(mFilterOptions);
670            mManageApplications.mSpinnerHeader.setVisibility(
671                    mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
672            notifyDataSetChanged();
673            if (mFilterOptions.size() == 1) {
674                if (DEBUG) Log.d(TAG, "Auto selecting filter " + filter);
675                mManageApplications.mFilterSpinner.setSelection(0);
676                mManageApplications.onItemSelected(null, null, 0, 0);
677            }
678        }
679
680        public void disableFilter(int filter) {
681            if (!mFilterOptions.remove((Integer) filter)) {
682                return;
683            }
684            if (DEBUG) Log.d(TAG, "Disabling filter " + filter);
685            Collections.sort(mFilterOptions);
686            mManageApplications.mSpinnerHeader.setVisibility(
687                    mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
688            notifyDataSetChanged();
689            if (mManageApplications.mFilter == filter) {
690                if (mFilterOptions.size() > 0) {
691                    if (DEBUG) Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0));
692                    mManageApplications.mFilterSpinner.setSelection(0);
693                    mManageApplications.onItemSelected(null, null, 0, 0);
694                }
695            }
696        }
697
698        @Override
699        public int getCount() {
700            return mFilterOptions.size();
701        }
702
703        @Override
704        public CharSequence getItem(int position) {
705            return getFilterString(mFilterOptions.get(position));
706        }
707
708        private CharSequence getFilterString(int filter) {
709            return mManageApplications.getString(FILTER_LABELS[filter]);
710        }
711
712    }
713
714    /*
715     * Custom adapter implementation for the ListView
716     * This adapter maintains a map for each displayed application and its properties
717     * An index value on each AppInfo object indicates the correct position or index
718     * in the list. If the list gets updated dynamically when the user is viewing the list of
719     * applications, we need to return the correct index of position. This is done by mapping
720     * the getId methods via the package name into the internal maps and indices.
721     * The order of applications in the list is mirrored in mAppLocalList
722     */
723    static class ApplicationsAdapter extends BaseAdapter implements Filterable,
724            ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
725            AbsListView.RecyclerListener, SectionIndexer {
726        private static final SectionInfo[] EMPTY_SECTIONS = new SectionInfo[0];
727
728        private final ApplicationsState mState;
729        private final ApplicationsState.Session mSession;
730        private final ManageApplications mManageApplications;
731        private final Context mContext;
732        private final ArrayList<View> mActive = new ArrayList<View>();
733        private final AppStateBaseBridge mExtraInfoBridge;
734        private final Handler mBgHandler;
735        private final Handler mFgHandler;
736        private int mFilterMode;
737        private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
738        private ArrayList<ApplicationsState.AppEntry> mEntries;
739        private boolean mResumed;
740        private int mLastSortMode = -1;
741        private int mWhichSize = SIZE_TOTAL;
742        CharSequence mCurFilterPrefix;
743        private PackageManager mPm;
744        private AppFilter mOverrideFilter;
745        private boolean mHasReceivedLoadEntries;
746        private boolean mHasReceivedBridgeCallback;
747
748        private AlphabeticIndex.ImmutableIndex<Locale> mIndex;
749        private SectionInfo[] mSections = EMPTY_SECTIONS;
750        private int[] mPositionToSectionIndex;
751
752        private Filter mFilter = new Filter() {
753            @Override
754            protected FilterResults performFiltering(CharSequence constraint) {
755                ArrayList<ApplicationsState.AppEntry> entries
756                        = applyPrefixFilter(constraint, mBaseEntries);
757                FilterResults fr = new FilterResults();
758                fr.values = entries;
759                fr.count = entries.size();
760                return fr;
761            }
762
763            @Override
764            @SuppressWarnings("unchecked")
765            protected void publishResults(CharSequence constraint, FilterResults results) {
766                mCurFilterPrefix = constraint;
767                mEntries = (ArrayList<ApplicationsState.AppEntry>) results.values;
768                rebuildSections();
769                notifyDataSetChanged();
770            }
771        };
772
773        public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
774                                   int filterMode) {
775            mState = state;
776            mFgHandler = new Handler();
777            mBgHandler = new Handler(mState.getBackgroundLooper());
778            mSession = state.newSession(this);
779            mManageApplications = manageApplications;
780            mContext = manageApplications.getActivity();
781            mPm = mContext.getPackageManager();
782            mFilterMode = filterMode;
783            if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
784                mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this,
785                        manageApplications.mNotifBackend);
786            } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
787                mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
788            } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) {
789                mExtraInfoBridge = new AppStatePowerBridge(mState, this);
790            } else if (mManageApplications.mListType == LIST_TYPE_OVERLAY) {
791                mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this);
792            } else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) {
793                mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this);
794            } else {
795                mExtraInfoBridge = null;
796            }
797        }
798
799        public void setOverrideFilter(AppFilter overrideFilter) {
800            mOverrideFilter = overrideFilter;
801            rebuild(true);
802        }
803
804        public void setFilter(int filter) {
805            mFilterMode = filter;
806            rebuild(true);
807        }
808
809        public void resume(int sort) {
810            if (DEBUG) Log.i(TAG, "Resume!  mResumed=" + mResumed);
811            if (!mResumed) {
812                mResumed = true;
813                mSession.resume();
814                mLastSortMode = sort;
815                if (mExtraInfoBridge != null) {
816                    mExtraInfoBridge.resume();
817                }
818                rebuild(false);
819            } else {
820                rebuild(sort);
821            }
822        }
823
824        public void pause() {
825            if (mResumed) {
826                mResumed = false;
827                mSession.pause();
828                if (mExtraInfoBridge != null) {
829                    mExtraInfoBridge.pause();
830                }
831            }
832        }
833
834        public void release() {
835            mSession.release();
836            if (mExtraInfoBridge != null) {
837                mExtraInfoBridge.release();
838            }
839        }
840
841        public void rebuild(int sort) {
842            if (sort == mLastSortMode) {
843                return;
844            }
845            mLastSortMode = sort;
846            rebuild(true);
847        }
848
849        public void rebuild(boolean eraseold) {
850            if (!mHasReceivedLoadEntries
851                    || (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) {
852                // Don't rebuild the list until all the app entries are loaded.
853                return;
854            }
855            if (DEBUG) Log.i(TAG, "Rebuilding app list...");
856            ApplicationsState.AppFilter filterObj;
857            Comparator<AppEntry> comparatorObj;
858            boolean emulated = Environment.isExternalStorageEmulated();
859            if (emulated) {
860                mWhichSize = SIZE_TOTAL;
861            } else {
862                mWhichSize = SIZE_INTERNAL;
863            }
864            filterObj = FILTERS[mFilterMode];
865            if (mOverrideFilter != null) {
866                filterObj = mOverrideFilter;
867            }
868            if (!mManageApplications.mShowSystem) {
869                filterObj = new CompoundFilter(filterObj,
870                        ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
871            }
872            switch (mLastSortMode) {
873                case R.id.sort_order_size:
874                    switch (mWhichSize) {
875                        case SIZE_INTERNAL:
876                            comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
877                            break;
878                        case SIZE_EXTERNAL:
879                            comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
880                            break;
881                        default:
882                            comparatorObj = ApplicationsState.SIZE_COMPARATOR;
883                            break;
884                    }
885                    break;
886                default:
887                    comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
888                    break;
889            }
890            filterObj = new CompoundFilter(filterObj, ApplicationsState.FILTER_NOT_HIDE);
891
892            AppFilter finalFilterObj = filterObj;
893            mBgHandler.post(() -> {
894                final ArrayList<AppEntry> entries = mSession.rebuild(finalFilterObj,
895                        comparatorObj, false);
896                if (entries != null) {
897                    mFgHandler.post(() -> onRebuildComplete(entries));
898                }
899            });
900        }
901
902
903        static private boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) {
904            if (info1 == null || info2 == null) {
905                return false;
906            }
907            if (info1.packageName == null || info2.packageName == null) {
908                return false;
909            }
910            return info1.packageName.equals(info2.packageName);
911        }
912
913        private ArrayList<ApplicationsState.AppEntry> removeDuplicateIgnoringUser(
914                ArrayList<ApplicationsState.AppEntry> entries)
915        {
916            int size = entries.size();
917            // returnList will not have more entries than entries
918            ArrayList<ApplicationsState.AppEntry> returnEntries = new
919                    ArrayList<ApplicationsState.AppEntry>(size);
920
921            // assume appinfo of same package but different users are grouped together
922            PackageItemInfo lastInfo = null;
923            for (int i = 0; i < size; i++) {
924                AppEntry appEntry = entries.get(i);
925                PackageItemInfo info = appEntry.info;
926                if (!packageNameEquals(lastInfo, appEntry.info)) {
927                    returnEntries.add(appEntry);
928                }
929                lastInfo = info;
930            }
931            returnEntries.trimToSize();
932            return returnEntries;
933        }
934
935        @Override
936        public void onRebuildComplete(ArrayList<AppEntry> entries) {
937            if (mFilterMode == FILTER_APPS_POWER_WHITELIST ||
938                    mFilterMode == FILTER_APPS_POWER_WHITELIST_ALL) {
939                entries = removeDuplicateIgnoringUser(entries);
940            }
941            mBaseEntries = entries;
942            if (mBaseEntries != null) {
943                mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
944                rebuildSections();
945            } else {
946                mEntries = null;
947                mSections = EMPTY_SECTIONS;
948                mPositionToSectionIndex = null;
949            }
950
951            notifyDataSetChanged();
952
953            if (mSession.getAllApps().size() != 0
954                    && mManageApplications.mListContainer.getVisibility() != View.VISIBLE) {
955                Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
956                        mManageApplications.mListContainer, true, true);
957            }
958            if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
959                // No enabled or disabled filters for usage access.
960                return;
961            }
962
963            mManageApplications.setHasDisabled(mState.haveDisabledApps());
964        }
965
966        private void rebuildSections() {
967            if (mEntries!= null && mManageApplications.mListView.isFastScrollEnabled()) {
968                // Rebuild sections
969                if (mIndex == null) {
970                    LocaleList locales = mContext.getResources().getConfiguration().getLocales();
971                    if (locales.size() == 0) {
972                        locales = new LocaleList(Locale.ENGLISH);
973                    }
974                    AlphabeticIndex<Locale> index = new AlphabeticIndex<>(locales.get(0));
975                    int localeCount = locales.size();
976                    for (int i = 1; i < localeCount; i++) {
977                        index.addLabels(locales.get(i));
978                    }
979                    // Ensure we always have some base English locale buckets
980                    index.addLabels(Locale.ENGLISH);
981                    mIndex = index.buildImmutableIndex();
982                }
983
984                ArrayList<SectionInfo> sections = new ArrayList<>();
985                int lastSecId = -1;
986                int totalEntries = mEntries.size();
987                mPositionToSectionIndex = new int[totalEntries];
988
989                for (int pos = 0; pos < totalEntries; pos++) {
990                    String label = mEntries.get(pos).label;
991                    int secId = mIndex.getBucketIndex(TextUtils.isEmpty(label) ? "" : label);
992                    if (secId != lastSecId) {
993                        lastSecId = secId;
994                        sections.add(new SectionInfo(mIndex.getBucket(secId).getLabel(), pos));
995                    }
996                    mPositionToSectionIndex[pos] = sections.size() - 1;
997                }
998                mSections = sections.toArray(EMPTY_SECTIONS);
999            } else {
1000                mSections = EMPTY_SECTIONS;
1001                mPositionToSectionIndex = null;
1002            }
1003        }
1004
1005        private void updateLoading() {
1006            Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
1007                    mManageApplications.mListContainer,
1008                    mHasReceivedLoadEntries && mSession.getAllApps().size() != 0, false);
1009        }
1010
1011        ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
1012                                                                ArrayList<ApplicationsState.AppEntry> origEntries) {
1013            if (prefix == null || prefix.length() == 0) {
1014                return origEntries;
1015            } else {
1016                String prefixStr = ApplicationsState.normalize(prefix.toString());
1017                final String spacePrefixStr = " " + prefixStr;
1018                ArrayList<ApplicationsState.AppEntry> newEntries
1019                        = new ArrayList<ApplicationsState.AppEntry>();
1020                for (int i = 0; i < origEntries.size(); i++) {
1021                    ApplicationsState.AppEntry entry = origEntries.get(i);
1022                    String nlabel = entry.getNormalizedLabel();
1023                    if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
1024                        newEntries.add(entry);
1025                    }
1026                }
1027                return newEntries;
1028            }
1029        }
1030
1031        @Override
1032        public void onExtraInfoUpdated() {
1033            mHasReceivedBridgeCallback = true;
1034            rebuild(false);
1035        }
1036
1037        @Override
1038        public void onRunningStateChanged(boolean running) {
1039            mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running);
1040        }
1041
1042        @Override
1043        public void onPackageListChanged() {
1044            rebuild(false);
1045        }
1046
1047        @Override
1048        public void onPackageIconChanged() {
1049            // We ensure icons are loaded when their item is displayed, so
1050            // don't care about icons loaded in the background.
1051        }
1052
1053        @Override
1054        public void onLoadEntriesCompleted() {
1055            mHasReceivedLoadEntries = true;
1056            // We may have been skipping rebuilds until this came in, trigger one now.
1057            rebuild(false);
1058        }
1059
1060        @Override
1061        public void onPackageSizeChanged(String packageName) {
1062            for (int i = 0; i < mActive.size(); i++) {
1063                AppViewHolder holder = (AppViewHolder) mActive.get(i).getTag();
1064                if (holder.entry.info.packageName.equals(packageName)) {
1065                    synchronized (holder.entry) {
1066                        updateSummary(holder);
1067                    }
1068                    if (holder.entry.info.packageName.equals(mManageApplications.mCurrentPkgName)
1069                            && mLastSortMode == R.id.sort_order_size) {
1070                        // We got the size information for the last app the
1071                        // user viewed, and are sorting by size...  they may
1072                        // have cleared data, so we immediately want to resort
1073                        // the list with the new size to reflect it to the user.
1074                        rebuild(false);
1075                    }
1076                    return;
1077                }
1078            }
1079        }
1080
1081        @Override
1082        public void onLauncherInfoChanged() {
1083            if (!mManageApplications.mShowSystem) {
1084                rebuild(false);
1085            }
1086        }
1087
1088        @Override
1089        public void onAllSizesComputed() {
1090            if (mLastSortMode == R.id.sort_order_size) {
1091                rebuild(false);
1092            }
1093        }
1094
1095        public int getCount() {
1096            return mEntries != null ? mEntries.size() : 0;
1097        }
1098
1099        public Object getItem(int position) {
1100            return mEntries.get(position);
1101        }
1102
1103        public ApplicationsState.AppEntry getAppEntry(int position) {
1104            return mEntries.get(position);
1105        }
1106
1107        public long getItemId(int position) {
1108            return mEntries.get(position).id;
1109        }
1110
1111        @Override
1112        public boolean areAllItemsEnabled() {
1113            return false;
1114        }
1115
1116        @Override
1117        public boolean isEnabled(int position) {
1118            if (mManageApplications.mListType != LIST_TYPE_HIGH_POWER) {
1119                return true;
1120            }
1121            ApplicationsState.AppEntry entry = mEntries.get(position);
1122            return !PowerWhitelistBackend.getInstance().isSysWhitelisted(entry.info.packageName);
1123        }
1124
1125        public View getView(int position, View convertView, ViewGroup parent) {
1126            // A ViewHolder keeps references to children views to avoid unnecessary calls
1127            // to findViewById() on each row.
1128            AppViewHolder holder = AppViewHolder.createOrRecycle(mManageApplications.mInflater,
1129                    convertView);
1130            convertView = holder.rootView;
1131
1132            // Bind the data efficiently with the holder
1133            ApplicationsState.AppEntry entry = mEntries.get(position);
1134            synchronized (entry) {
1135                holder.entry = entry;
1136                if (entry.label != null) {
1137                    holder.appName.setText(entry.label);
1138                }
1139                mState.ensureIcon(entry);
1140                if (entry.icon != null) {
1141                    holder.appIcon.setImageDrawable(entry.icon);
1142                }
1143                updateSummary(holder);
1144                if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
1145                    holder.disabled.setVisibility(View.VISIBLE);
1146                    holder.disabled.setText(R.string.not_installed);
1147                } else if (!entry.info.enabled) {
1148                    holder.disabled.setVisibility(View.VISIBLE);
1149                    holder.disabled.setText(R.string.disabled);
1150                } else {
1151                    holder.disabled.setVisibility(View.GONE);
1152                }
1153            }
1154            mActive.remove(convertView);
1155            mActive.add(convertView);
1156            convertView.setEnabled(isEnabled(position));
1157            return convertView;
1158        }
1159
1160        private void updateSummary(AppViewHolder holder) {
1161            switch (mManageApplications.mListType) {
1162                case LIST_TYPE_NOTIFICATION:
1163                    if (holder.entry.extraInfo != null) {
1164                        holder.summary.setText(InstalledAppDetails.getNotificationSummary(
1165                                (AppRow) holder.entry.extraInfo, mContext));
1166                    } else {
1167                        holder.summary.setText(null);
1168                    }
1169                    break;
1170
1171                case LIST_TYPE_USAGE_ACCESS:
1172                    if (holder.entry.extraInfo != null) {
1173                        holder.summary.setText((new UsageState((PermissionState) holder.entry
1174                                .extraInfo)).isPermissible() ? R.string.switch_on_text :
1175                                R.string.switch_off_text);
1176                    } else {
1177                        holder.summary.setText(null);
1178                    }
1179                    break;
1180
1181                case LIST_TYPE_HIGH_POWER:
1182                    holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry));
1183                    break;
1184
1185                case LIST_TYPE_OVERLAY:
1186                    holder.summary.setText(DrawOverlayDetails.getSummary(mContext, holder.entry));
1187                    break;
1188
1189                case LIST_TYPE_WRITE_SETTINGS:
1190                    holder.summary.setText(WriteSettingsDetails.getSummary(mContext,
1191                            holder.entry));
1192                    break;
1193
1194                default:
1195                    holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
1196                    break;
1197            }
1198        }
1199
1200        @Override
1201        public Filter getFilter() {
1202            return mFilter;
1203        }
1204
1205        @Override
1206        public void onMovedToScrapHeap(View view) {
1207            mActive.remove(view);
1208        }
1209
1210        @Override
1211        public Object[] getSections() {
1212            return mSections;
1213        }
1214
1215        @Override
1216        public int getPositionForSection(int sectionIndex) {
1217            return mSections[sectionIndex].position;
1218        }
1219
1220        @Override
1221        public int getSectionForPosition(int position) {
1222            return mPositionToSectionIndex[position];
1223        }
1224    }
1225
1226    private static class SummaryProvider implements SummaryLoader.SummaryProvider {
1227
1228        private final Context mContext;
1229        private final SummaryLoader mLoader;
1230        private ApplicationsState.Session mSession;
1231
1232        private SummaryProvider(Context context, SummaryLoader loader) {
1233            mContext = context;
1234            mLoader = loader;
1235        }
1236
1237        @Override
1238        public void setListening(boolean listening) {
1239            if (listening) {
1240                new AppCounter(mContext) {
1241                    @Override
1242                    protected void onCountComplete(int num) {
1243                        mLoader.setSummary(SummaryProvider.this,
1244                                mContext.getString(R.string.apps_summary, num));
1245                    }
1246
1247                    @Override
1248                    protected boolean includeInCount(ApplicationInfo info) {
1249                        if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
1250                            return true;
1251                        } else if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
1252                            return true;
1253                        }
1254                        Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
1255                                .addCategory(Intent.CATEGORY_LAUNCHER)
1256                                .setPackage(info.packageName);
1257                        int userId = UserHandle.getUserId(info.uid);
1258                        List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
1259                                launchIntent,
1260                                PackageManager.GET_DISABLED_COMPONENTS
1261                                        | PackageManager.MATCH_DIRECT_BOOT_AWARE
1262                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
1263                                userId);
1264                        return intents != null && intents.size() != 0;
1265                    }
1266                }.execute();
1267            }
1268        }
1269    }
1270
1271    private static class SectionInfo {
1272        final String label;
1273        final int position;
1274
1275        public SectionInfo(String label, int position) {
1276            this.label = label;
1277            this.position = position;
1278        }
1279
1280        @Override
1281        public String toString() {
1282            return label;
1283        }
1284    }
1285
1286    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
1287            = new SummaryLoader.SummaryProviderFactory() {
1288        @Override
1289        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
1290                                                                   SummaryLoader summaryLoader) {
1291            return new SummaryProvider(activity, summaryLoader);
1292        }
1293    };
1294}
1295