2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.applications;
19 import android.app.Activity;
20 import android.app.usage.StorageStatsManager;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.PackageItemInfo;
25 import android.content.pm.PackageManager;
26 import android.content.pm.UserInfo;
27 import android.icu.text.AlphabeticIndex;
28 import android.os.Bundle;
29 import android.os.Environment;
30 import android.os.Handler;
31 import android.os.LocaleList;
32 import android.os.UserHandle;
33 import android.os.UserManager;
34 import android.preference.PreferenceFrameLayout;
35 import android.text.TextUtils;
36 import android.text.format.Formatter;
37 import android.util.Log;
38 import android.view.LayoutInflater;
39 import android.view.Menu;
40 import android.view.MenuInflater;
41 import android.view.MenuItem;
42 import android.view.View;
43 import android.view.ViewGroup;
44 import android.widget.AbsListView;
45 import android.widget.AdapterView;
46 import android.widget.AdapterView.OnItemClickListener;
47 import android.widget.AdapterView.OnItemSelectedListener;
48 import android.widget.ArrayAdapter;
49 import android.widget.BaseAdapter;
50 import android.widget.Filter;
51 import android.widget.Filterable;
52 import android.widget.FrameLayout;
53 import android.widget.ListView;
54 import android.widget.SectionIndexer;
55 import android.widget.Spinner;
57 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
58 import com.android.settings.AppHeader;
59 import com.android.settings.R;
60 import com.android.settings.Settings.AllApplicationsActivity;
61 import com.android.settings.Settings.GamesStorageActivity;
62 import com.android.settings.Settings.HighPowerApplicationsActivity;
63 import com.android.settings.Settings.ManageExternalSourcesActivity;
64 import com.android.settings.Settings.NotificationAppListActivity;
65 import com.android.settings.Settings.OverlaySettingsActivity;
66 import com.android.settings.Settings.StorageUseActivity;
67 import com.android.settings.Settings.UsageAccessSettingsActivity;
68 import com.android.settings.Settings.WriteSettingsActivity;
69 import com.android.settings.SettingsActivity;
70 import com.android.settings.Utils;
71 import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
72 import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState;
73 import com.android.settings.applications.AppStateUsageBridge.UsageState;
74 import com.android.settings.core.InstrumentedPreferenceFragment;
75 import com.android.settings.dashboard.SummaryLoader;
76 import com.android.settings.deviceinfo.storage.StorageStatsSource;
77 import com.android.settings.fuelgauge.HighPowerDetail;
78 import com.android.settings.fuelgauge.PowerWhitelistBackend;
79 import com.android.settings.notification.AppNotificationSettings;
80 import com.android.settings.notification.ConfigureNotificationSettings;
81 import com.android.settings.notification.NotificationBackend;
82 import com.android.settings.notification.NotificationBackend.AppRow;
83 import com.android.settings.overlay.FeatureFactory;
84 import com.android.settingslib.HelpUtils;
85 import com.android.settingslib.applications.ApplicationsState;
86 import com.android.settingslib.applications.ApplicationsState.AppEntry;
87 import com.android.settingslib.applications.ApplicationsState.AppFilter;
88 import com.android.settingslib.applications.ApplicationsState.CompoundFilter;
89 import com.android.settingslib.applications.ApplicationsState.VolumeFilter;
91 import java.util.ArrayList;
92 import java.util.Collections;
93 import java.util.Comparator;
94 import java.util.List;
95 import java.util.Locale;
98 * Activity to pick an application that will be used to display installation information and
99 * options to uninstall/delete user data for system applications. This activity
100 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
103 public class ManageApplications extends InstrumentedPreferenceFragment
104 implements OnItemClickListener, OnItemSelectedListener {
106 static final String TAG = "ManageApplications";
107 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
110 public static final String EXTRA_CLASSNAME = "classname";
111 // Used for storage only.
112 public static final String EXTRA_VOLUME_UUID = "volumeUuid";
113 public static final String EXTRA_VOLUME_NAME = "volumeName";
114 public static final String EXTRA_STORAGE_TYPE = "storageType";
116 private static final String EXTRA_SORT_ORDER = "sortOrder";
117 private static final String EXTRA_SHOW_SYSTEM = "showSystem";
118 private static final String EXTRA_HAS_ENTRIES = "hasEntries";
119 private static final String EXTRA_HAS_BRIDGE = "hasBridge";
121 // attributes used as keys when passing values to InstalledAppDetails activity
122 public static final String APP_CHG = "chg";
124 // constant value that can be used to check return code from sub activity.
125 private static final int INSTALLED_APP_DETAILS = 1;
126 private static final int ADVANCED_SETTINGS = 2;
128 public static final int SIZE_TOTAL = 0;
129 public static final int SIZE_INTERNAL = 1;
130 public static final int SIZE_EXTERNAL = 2;
132 // Filter options used for displayed list of applications
133 // The order which they appear is the order they will show when spinner is present.
134 public static final int FILTER_APPS_POWER_WHITELIST = 0;
135 public static final int FILTER_APPS_POWER_WHITELIST_ALL = 1;
136 public static final int FILTER_APPS_ALL = 2;
137 public static final int FILTER_APPS_ENABLED = 3;
138 public static final int FILTER_APPS_DISABLED = 4;
139 public static final int FILTER_APPS_BLOCKED = 5;
140 public static final int FILTER_APPS_PERSONAL = 6;
141 public static final int FILTER_APPS_WORK = 7;
142 public static final int FILTER_APPS_USAGE_ACCESS = 8;
143 public static final int FILTER_APPS_WITH_OVERLAY = 9;
144 public static final int FILTER_APPS_WRITE_SETTINGS = 10;
145 public static final int FILTER_APPS_INSTALL_SOURCES = 12;
147 // Storage types. Used to determine what the extra item in the list of preferences is.
148 public static final int STORAGE_TYPE_DEFAULT = 0;
149 public static final int STORAGE_TYPE_MUSIC = 1;
151 // This is the string labels for the filter modes above, the order must be kept in sync.
152 public static final int[] FILTER_LABELS = new int[]{
153 R.string.high_power_filter_on, // High power whitelist, on
154 R.string.filter_all_apps, // Without disabled until used
155 R.string.filter_all_apps, // All apps
156 R.string.filter_enabled_apps, // Enabled
157 R.string.filter_apps_disabled, // Disabled
158 R.string.filter_notif_blocked_apps, // Blocked Notifications
159 R.string.filter_personal_apps, // Personal
160 R.string.filter_work_apps, // Work
161 R.string.filter_with_domain_urls_apps, // Domain URLs
162 R.string.filter_all_apps, // Usage access screen, never displayed
163 R.string.filter_overlay_apps, // Apps with overlay permission
164 R.string.filter_write_settings_apps, // Apps that can write system settings
165 R.string.filter_install_sources_apps, // Apps that are trusted sources of apks
167 // This is the actual mapping to filters from FILTER_ constants above, the order must
169 public static final AppFilter[] FILTERS = new AppFilter[]{
170 new CompoundFilter(AppStatePowerBridge.FILTER_POWER_WHITELISTED,
171 ApplicationsState.FILTER_ALL_ENABLED), // High power whitelist, on
172 new CompoundFilter(ApplicationsState.FILTER_WITHOUT_DISABLED_UNTIL_USED,
173 ApplicationsState.FILTER_ALL_ENABLED), // Without disabled until used
174 ApplicationsState.FILTER_EVERYTHING, // All apps
175 ApplicationsState.FILTER_ALL_ENABLED, // Enabled
176 ApplicationsState.FILTER_DISABLED, // Disabled
177 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED, // Blocked Notifications
178 ApplicationsState.FILTER_PERSONAL, // Personal
179 ApplicationsState.FILTER_WORK, // Work
180 ApplicationsState.FILTER_WITH_DOMAIN_URLS, // Apps with Domain URLs
181 AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
182 AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW, // Apps that can draw overlays
183 AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS, // Apps that can write system settings
184 AppStateInstallAppsBridge.FILTER_APP_SOURCES,
188 private int mSortOrder = R.id.sort_order_alpha;
190 // whether showing system apps.
191 private boolean mShowSystem;
193 private ApplicationsState mApplicationsState;
195 public int mListType;
198 public ApplicationsAdapter mApplications;
200 private View mLoadingContainer;
202 private View mListContainer;
204 // ListView used to display list
205 private ListView mListView;
207 // Size resource used for packages whose size computation failed for some reason
208 CharSequence mInvalidSizeStr;
210 // layout inflater object used to inflate views
211 private LayoutInflater mInflater;
213 private String mCurrentPkgName;
214 private int mCurrentUid;
215 private boolean mFinishAfterDialog;
217 private Menu mOptionsMenu;
219 public static final int LIST_TYPE_MAIN = 0;
220 public static final int LIST_TYPE_NOTIFICATION = 1;
221 public static final int LIST_TYPE_STORAGE = 3;
222 public static final int LIST_TYPE_USAGE_ACCESS = 4;
223 public static final int LIST_TYPE_HIGH_POWER = 5;
224 public static final int LIST_TYPE_OVERLAY = 6;
225 public static final int LIST_TYPE_WRITE_SETTINGS = 7;
226 public static final int LIST_TYPE_MANAGE_SOURCES = 8;
227 public static final int LIST_TYPE_GAMES = 9;
229 private View mRootView;
231 private View mSpinnerHeader;
232 private Spinner mFilterSpinner;
233 private FilterSpinnerAdapter mFilterAdapter;
234 private NotificationBackend mNotifBackend;
235 private ResetAppsHelper mResetAppsHelper;
236 private String mVolumeUuid;
237 private String mVolumeName;
238 private int mStorageType;
241 public void onCreate(Bundle savedInstanceState) {
242 super.onCreate(savedInstanceState);
243 setHasOptionsMenu(true);
244 mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
246 Intent intent = getActivity().getIntent();
247 Bundle args = getArguments();
248 String className = args != null ? args.getString(EXTRA_CLASSNAME) : null;
249 if (className == null) {
250 className = intent.getComponent().getClassName();
252 if (className.equals(AllApplicationsActivity.class.getName())) {
254 } else if (className.equals(NotificationAppListActivity.class.getName())
255 || this instanceof NotificationApps) {
256 mListType = LIST_TYPE_NOTIFICATION;
257 mNotifBackend = new NotificationBackend();
258 } else if (className.equals(StorageUseActivity.class.getName())) {
259 if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) {
260 mVolumeUuid = args.getString(EXTRA_VOLUME_UUID);
261 mVolumeName = args.getString(EXTRA_VOLUME_NAME);
262 mStorageType = args.getInt(EXTRA_STORAGE_TYPE, STORAGE_TYPE_DEFAULT);
263 mListType = LIST_TYPE_STORAGE;
265 // No volume selected, display a normal list, sorted by size.
266 mListType = LIST_TYPE_MAIN;
268 mSortOrder = R.id.sort_order_size;
269 } else if (className.equals(UsageAccessSettingsActivity.class.getName())) {
270 mListType = LIST_TYPE_USAGE_ACCESS;
271 } else if (className.equals(HighPowerApplicationsActivity.class.getName())) {
272 mListType = LIST_TYPE_HIGH_POWER;
273 // Default to showing system.
275 } else if (className.equals(OverlaySettingsActivity.class.getName())) {
276 mListType = LIST_TYPE_OVERLAY;
277 } else if (className.equals(WriteSettingsActivity.class.getName())) {
278 mListType = LIST_TYPE_WRITE_SETTINGS;
279 } else if (className.equals(ManageExternalSourcesActivity.class.getName())) {
280 mListType = LIST_TYPE_MANAGE_SOURCES;
281 } else if (className.equals(GamesStorageActivity.class.getName())) {
282 mListType = LIST_TYPE_GAMES;
283 mSortOrder = R.id.sort_order_size;
285 mListType = LIST_TYPE_MAIN;
287 mFilter = getDefaultFilter();
289 if (savedInstanceState != null) {
290 mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
291 mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
294 mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
296 mResetAppsHelper = new ResetAppsHelper(getActivity());
301 public View onCreateView(LayoutInflater inflater, ViewGroup container,
302 Bundle savedInstanceState) {
303 // initialize the inflater
304 mInflater = inflater;
306 mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
307 mLoadingContainer = mRootView.findViewById(R.id.loading_container);
308 mLoadingContainer.setVisibility(View.VISIBLE);
309 mListContainer = mRootView.findViewById(R.id.list_container);
310 if (mListContainer != null) {
311 // Create adapter and list view here
312 View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
313 ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
314 if (emptyView != null) {
315 lv.setEmptyView(emptyView);
317 lv.setOnItemClickListener(this);
318 lv.setSaveEnabled(true);
319 lv.setItemsCanFocus(true);
320 lv.setTextFilterEnabled(true);
322 mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
323 if (savedInstanceState != null) {
324 mApplications.mHasReceivedLoadEntries =
325 savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false);
326 mApplications.mHasReceivedBridgeCallback =
327 savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false);
329 if (mStorageType == STORAGE_TYPE_MUSIC) {
330 Context context = getContext();
331 mApplications.setExtraViewController(new MusicViewHolderController(
333 new StorageStatsSource(context),
336 mListView.setAdapter(mApplications);
337 mListView.setRecyclerListener(mApplications);
338 mListView.setFastScrollEnabled(isFastScrollEnabled());
340 Utils.prepareCustomPreferencesList(container, mRootView, mListView, false);
343 // We have to do this now because PreferenceFrameLayout looks at it
344 // only when the view is added.
345 if (container instanceof PreferenceFrameLayout) {
346 ((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true;
351 mResetAppsHelper.onRestoreInstanceState(savedInstanceState);
356 private void createHeader() {
357 Activity activity = getActivity();
358 FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
359 mSpinnerHeader = activity.getLayoutInflater()
360 .inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
361 mFilterSpinner = (Spinner) mSpinnerHeader.findViewById(R.id.filter_spinner);
362 mFilterAdapter = new FilterSpinnerAdapter(this);
363 mFilterSpinner.setAdapter(mFilterAdapter);
364 mFilterSpinner.setOnItemSelectedListener(this);
365 pinnedHeader.addView(mSpinnerHeader, 0);
367 mFilterAdapter.enableFilter(getDefaultFilter());
368 if (mListType == LIST_TYPE_MAIN) {
369 if (UserManager.get(getActivity()).getUserProfiles().size() > 1) {
370 mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL);
371 mFilterAdapter.enableFilter(FILTER_APPS_WORK);
374 if (mListType == LIST_TYPE_NOTIFICATION) {
375 mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED);
377 if (mListType == LIST_TYPE_HIGH_POWER) {
378 mFilterAdapter.enableFilter(FILTER_APPS_POWER_WHITELIST_ALL);
380 if (mListType == LIST_TYPE_STORAGE) {
381 AppFilter filter = new VolumeFilter(mVolumeUuid);
382 if (mStorageType == STORAGE_TYPE_MUSIC) {
383 filter = new CompoundFilter(ApplicationsState.FILTER_AUDIO, filter);
385 mApplications.setOverrideFilter(filter);
387 if (mListType == LIST_TYPE_GAMES) {
388 mApplications.setOverrideFilter(ApplicationsState.FILTER_GAMES);
393 public void onViewCreated(View view, Bundle savedInstanceState) {
394 super.onViewCreated(view, savedInstanceState);
396 if (mListType == LIST_TYPE_STORAGE) {
397 final Activity activity = getActivity();
398 final boolean isNewIAEnabled = FeatureFactory.getFactory(activity)
399 .getDashboardFeatureProvider(activity)
401 if (!isNewIAEnabled) {
402 FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
403 AppHeader.createAppHeader(getActivity(), null, mVolumeName, null, -1, pinnedHeader);
408 private int getDefaultFilter() {
410 case LIST_TYPE_USAGE_ACCESS:
411 return FILTER_APPS_USAGE_ACCESS;
412 case LIST_TYPE_HIGH_POWER:
413 return FILTER_APPS_POWER_WHITELIST;
414 case LIST_TYPE_OVERLAY:
415 return FILTER_APPS_WITH_OVERLAY;
416 case LIST_TYPE_WRITE_SETTINGS:
417 return FILTER_APPS_WRITE_SETTINGS;
418 case LIST_TYPE_MANAGE_SOURCES:
419 return FILTER_APPS_INSTALL_SOURCES;
421 return FILTER_APPS_ALL;
425 private boolean isFastScrollEnabled() {
428 case LIST_TYPE_NOTIFICATION:
429 case LIST_TYPE_STORAGE:
430 case LIST_TYPE_GAMES:
431 return mSortOrder == R.id.sort_order_alpha;
438 public int getMetricsCategory() {
441 return MetricsEvent.MANAGE_APPLICATIONS;
442 case LIST_TYPE_NOTIFICATION:
443 return MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS;
444 case LIST_TYPE_STORAGE:
445 case LIST_TYPE_GAMES:
446 return MetricsEvent.APPLICATIONS_STORAGE_APPS;
447 case LIST_TYPE_USAGE_ACCESS:
448 return MetricsEvent.USAGE_ACCESS;
449 case LIST_TYPE_HIGH_POWER:
450 return MetricsEvent.APPLICATIONS_HIGH_POWER_APPS;
451 case LIST_TYPE_OVERLAY:
452 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
453 case LIST_TYPE_WRITE_SETTINGS:
454 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
455 case LIST_TYPE_MANAGE_SOURCES:
456 return MetricsEvent.MANAGE_EXTERNAL_SOURCES;
458 return MetricsEvent.VIEW_UNKNOWN;
463 public void onResume() {
467 if (mApplications != null) {
468 mApplications.resume(mSortOrder);
469 mApplications.updateLoading();
474 public void onSaveInstanceState(Bundle outState) {
475 super.onSaveInstanceState(outState);
476 mResetAppsHelper.onSaveInstanceState(outState);
477 outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
478 outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
479 outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries);
480 outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback);
484 public void onPause() {
486 if (mApplications != null) {
487 mApplications.pause();
492 public void onStop() {
494 mResetAppsHelper.stop();
498 public void onDestroyView() {
499 super.onDestroyView();
501 if (mApplications != null) {
502 mApplications.release();
508 public void onActivityResult(int requestCode, int resultCode, Intent data) {
509 if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
510 if (mListType == LIST_TYPE_NOTIFICATION) {
511 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
512 } else if (mListType == LIST_TYPE_HIGH_POWER || mListType == LIST_TYPE_OVERLAY
513 || mListType == LIST_TYPE_WRITE_SETTINGS) {
514 if (mFinishAfterDialog) {
515 getActivity().onBackPressed();
517 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
520 mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid));
525 // utility method used to start sub activity
526 private void startApplicationDetailsActivity() {
528 case LIST_TYPE_NOTIFICATION:
529 startAppInfoFragment(AppNotificationSettings.class,
530 R.string.app_notifications_title);
532 case LIST_TYPE_USAGE_ACCESS:
533 startAppInfoFragment(UsageAccessDetails.class, R.string.usage_access);
535 case LIST_TYPE_STORAGE:
536 startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings);
538 case LIST_TYPE_HIGH_POWER:
539 HighPowerDetail.show(this, mCurrentPkgName, INSTALLED_APP_DETAILS,
542 case LIST_TYPE_OVERLAY:
543 startAppInfoFragment(DrawOverlayDetails.class, R.string.overlay_settings);
545 case LIST_TYPE_WRITE_SETTINGS:
546 startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings);
548 case LIST_TYPE_MANAGE_SOURCES:
549 startAppInfoFragment(ExternalSourcesDetails.class, R.string.install_other_apps);
551 case LIST_TYPE_GAMES:
552 startAppInfoFragment(AppStorageSettings.class, R.string.game_storage_settings);
554 // TODO: Figure out if there is a way where we can spin up the profile's settings
555 // process ahead of time, to avoid a long load of data when user clicks on a managed app.
556 // Maybe when they load the list of apps that contains managed profile apps.
558 startAppInfoFragment(InstalledAppDetails.class, R.string.application_info_label);
563 private void startAppInfoFragment(Class<?> fragment, int titleRes) {
564 AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this,
565 INSTALLED_APP_DETAILS);
569 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
570 HelpUtils.prepareHelpMenuItem(getActivity(), menu, mListType == LIST_TYPE_MAIN
571 ? R.string.help_uri_apps : R.string.help_uri_notifications, getClass().getName());
573 inflater.inflate(R.menu.manage_apps, menu);
578 public void onPrepareOptionsMenu(Menu menu) {
583 public void onDestroyOptionsMenu() {
587 void updateOptionsMenu() {
588 if (mOptionsMenu == null) {
591 final Context context = getActivity();
592 if (FeatureFactory.getFactory(context).getDashboardFeatureProvider(context).isEnabled()) {
593 mOptionsMenu.findItem(R.id.advanced).setVisible(false);
595 mOptionsMenu.findItem(R.id.advanced).setVisible(
596 mListType == LIST_TYPE_MAIN || mListType == LIST_TYPE_NOTIFICATION);
599 mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE
600 && mSortOrder != R.id.sort_order_alpha);
601 mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE
602 && mSortOrder != R.id.sort_order_size);
604 mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem
605 && mListType != LIST_TYPE_HIGH_POWER);
606 mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem
607 && mListType != LIST_TYPE_HIGH_POWER);
611 public boolean onOptionsItemSelected(MenuItem item) {
612 int menuId = item.getItemId();
613 switch (item.getItemId()) {
614 case R.id.sort_order_alpha:
615 case R.id.sort_order_size:
617 mListView.setFastScrollEnabled(isFastScrollEnabled());
618 if (mApplications != null) {
619 mApplications.rebuild(mSortOrder);
622 case R.id.show_system:
623 case R.id.hide_system:
624 mShowSystem = !mShowSystem;
625 mApplications.rebuild(false);
627 case R.id.reset_app_preferences:
628 mResetAppsHelper.buildResetDialog();
631 if (mListType == LIST_TYPE_NOTIFICATION) {
632 ((SettingsActivity) getActivity()).startPreferencePanel(
633 ConfigureNotificationSettings.class.getName(), null,
634 R.string.configure_notification_settings, null, this, ADVANCED_SETTINGS);
636 ((SettingsActivity) getActivity()).startPreferencePanel(
637 AdvancedAppSettings.class.getName(), null, R.string.configure_apps,
638 null, this, ADVANCED_SETTINGS);
642 // Handle the home button
650 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
651 if (mApplications == null) {
655 if (mApplications.getApplicationCount() > position) {
656 ApplicationsState.AppEntry entry = mApplications.getAppEntry(position);
657 mCurrentPkgName = entry.info.packageName;
658 mCurrentUid = entry.info.uid;
659 startApplicationDetailsActivity();
661 mApplications.mExtraViewController.onClick(this);
666 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
667 mFilter = mFilterAdapter.getFilter(position);
668 mApplications.setFilter(mFilter);
669 if (DEBUG) Log.d(TAG, "Selecting filter " + mFilter);
673 public void onNothingSelected(AdapterView<?> parent) {
676 public void updateView() {
678 final Activity host = getActivity();
680 host.invalidateOptionsMenu();
684 public void setHasDisabled(boolean hasDisabledApps) {
685 if (mListType != LIST_TYPE_MAIN) {
688 mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps);
689 mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps);
692 static class FilterSpinnerAdapter extends ArrayAdapter<CharSequence> {
694 private final ManageApplications mManageApplications;
696 // Use ArrayAdapter for view logic, but have our own list for managing
697 // the options available.
698 private final ArrayList<Integer> mFilterOptions = new ArrayList<>();
700 public FilterSpinnerAdapter(ManageApplications manageApplications) {
701 super(manageApplications.mFilterSpinner.getContext(), R.layout.filter_spinner_item);
702 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
703 mManageApplications = manageApplications;
706 public int getFilter(int position) {
707 return mFilterOptions.get(position);
710 public void setFilterEnabled(int filter, boolean enabled) {
712 enableFilter(filter);
714 disableFilter(filter);
718 public void enableFilter(int filter) {
719 if (mFilterOptions.contains(filter)) return;
720 if (DEBUG) Log.d(TAG, "Enabling filter " + filter);
721 mFilterOptions.add(filter);
722 Collections.sort(mFilterOptions);
723 mManageApplications.mSpinnerHeader.setVisibility(
724 mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
725 notifyDataSetChanged();
726 if (mFilterOptions.size() == 1) {
727 if (DEBUG) Log.d(TAG, "Auto selecting filter " + filter);
728 mManageApplications.mFilterSpinner.setSelection(0);
729 mManageApplications.onItemSelected(null, null, 0, 0);
733 public void disableFilter(int filter) {
734 if (!mFilterOptions.remove((Integer) filter)) {
737 if (DEBUG) Log.d(TAG, "Disabling filter " + filter);
738 Collections.sort(mFilterOptions);
739 mManageApplications.mSpinnerHeader.setVisibility(
740 mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
741 notifyDataSetChanged();
742 if (mManageApplications.mFilter == filter) {
743 if (mFilterOptions.size() > 0) {
744 if (DEBUG) Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0));
745 mManageApplications.mFilterSpinner.setSelection(0);
746 mManageApplications.onItemSelected(null, null, 0, 0);
752 public int getCount() {
753 return mFilterOptions.size();
757 public CharSequence getItem(int position) {
758 return getFilterString(mFilterOptions.get(position));
761 private CharSequence getFilterString(int filter) {
762 return mManageApplications.getString(FILTER_LABELS[filter]);
768 * Custom adapter implementation for the ListView
769 * This adapter maintains a map for each displayed application and its properties
770 * An index value on each AppInfo object indicates the correct position or index
771 * in the list. If the list gets updated dynamically when the user is viewing the list of
772 * applications, we need to return the correct index of position. This is done by mapping
773 * the getId methods via the package name into the internal maps and indices.
774 * The order of applications in the list is mirrored in mAppLocalList
776 static class ApplicationsAdapter extends BaseAdapter implements Filterable,
777 ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
778 AbsListView.RecyclerListener, SectionIndexer {
779 private static final SectionInfo[] EMPTY_SECTIONS = new SectionInfo[0];
781 private final ApplicationsState mState;
782 private final ApplicationsState.Session mSession;
783 private final ManageApplications mManageApplications;
784 private final Context mContext;
785 private final ArrayList<View> mActive = new ArrayList<View>();
786 private final AppStateBaseBridge mExtraInfoBridge;
787 private final Handler mBgHandler;
788 private final Handler mFgHandler;
789 private int mFilterMode;
790 private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
791 private ArrayList<ApplicationsState.AppEntry> mEntries;
792 private boolean mResumed;
793 private int mLastSortMode = -1;
794 private int mWhichSize = SIZE_TOTAL;
795 CharSequence mCurFilterPrefix;
796 private PackageManager mPm;
797 private AppFilter mOverrideFilter;
798 private boolean mHasReceivedLoadEntries;
799 private boolean mHasReceivedBridgeCallback;
800 private FileViewHolderController mExtraViewController;
802 // These two variables are used to remember and restore the last scroll position when this
803 // fragment is paused. We need this special handling because app entries are added gradually
804 // when we rebuild the list after the user made some changes, like uninstalling an app.
805 private int mLastIndex = -1;
806 private int mLastTop;
808 private AlphabeticIndex.ImmutableIndex<Locale> mIndex;
809 private SectionInfo[] mSections = EMPTY_SECTIONS;
810 private int[] mPositionToSectionIndex;
812 private Filter mFilter = new Filter() {
814 protected FilterResults performFiltering(CharSequence constraint) {
815 ArrayList<ApplicationsState.AppEntry> entries
816 = applyPrefixFilter(constraint, mBaseEntries);
817 FilterResults fr = new FilterResults();
819 fr.count = entries.size();
824 @SuppressWarnings("unchecked")
825 protected void publishResults(CharSequence constraint, FilterResults results) {
826 mCurFilterPrefix = constraint;
827 mEntries = (ArrayList<ApplicationsState.AppEntry>) results.values;
829 notifyDataSetChanged();
833 public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
836 mFgHandler = new Handler();
837 mBgHandler = new Handler(mState.getBackgroundLooper());
838 mSession = state.newSession(this);
839 mManageApplications = manageApplications;
840 mContext = manageApplications.getActivity();
841 mPm = mContext.getPackageManager();
842 mFilterMode = filterMode;
843 if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
844 mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this,
845 manageApplications.mNotifBackend);
846 } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
847 mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
848 } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) {
849 mExtraInfoBridge = new AppStatePowerBridge(mState, this);
850 } else if (mManageApplications.mListType == LIST_TYPE_OVERLAY) {
851 mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this);
852 } else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) {
853 mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this);
854 } else if (mManageApplications.mListType == LIST_TYPE_MANAGE_SOURCES) {
855 mExtraInfoBridge = new AppStateInstallAppsBridge(mContext, mState, this);
857 mExtraInfoBridge = null;
861 public void setOverrideFilter(AppFilter overrideFilter) {
862 mOverrideFilter = overrideFilter;
866 public void setFilter(int filter) {
867 mFilterMode = filter;
871 public void setExtraViewController(FileViewHolderController extraViewController) {
872 mExtraViewController = extraViewController;
875 public void resume(int sort) {
876 if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed);
880 mLastSortMode = sort;
881 if (mExtraInfoBridge != null) {
882 mExtraInfoBridge.resume();
890 public void pause() {
894 if (mExtraInfoBridge != null) {
895 mExtraInfoBridge.pause();
898 // Record the current scroll position before pausing.
899 mLastIndex = mManageApplications.mListView.getFirstVisiblePosition();
900 View v = mManageApplications.mListView.getChildAt(0);
901 mLastTop = (v == null) ? 0 : (v.getTop() - mManageApplications.mListView.getPaddingTop());
904 public void release() {
906 if (mExtraInfoBridge != null) {
907 mExtraInfoBridge.release();
911 public void rebuild(int sort) {
912 if (sort == mLastSortMode) {
915 mLastSortMode = sort;
919 public void rebuild(boolean eraseold) {
920 if (!mHasReceivedLoadEntries
921 || (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) {
922 // Don't rebuild the list until all the app entries are loaded.
925 ApplicationsState.AppFilter filterObj;
926 Comparator<AppEntry> comparatorObj;
927 boolean emulated = Environment.isExternalStorageEmulated();
929 mWhichSize = SIZE_TOTAL;
931 mWhichSize = SIZE_INTERNAL;
933 filterObj = FILTERS[mFilterMode];
934 if (mOverrideFilter != null) {
935 filterObj = mOverrideFilter;
937 if (!mManageApplications.mShowSystem) {
938 filterObj = new CompoundFilter(filterObj,
939 ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
941 switch (mLastSortMode) {
942 case R.id.sort_order_size:
943 switch (mWhichSize) {
945 comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
948 comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
951 comparatorObj = ApplicationsState.SIZE_COMPARATOR;
956 comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
960 if (mExtraViewController != null) {
961 mExtraViewController.queryStats();
964 filterObj = new CompoundFilter(filterObj, ApplicationsState.FILTER_NOT_HIDE);
965 AppFilter finalFilterObj = filterObj;
966 mBgHandler.post(() -> {
967 final ArrayList<AppEntry> entries = mSession.rebuild(finalFilterObj,
968 comparatorObj, false);
969 if (entries != null) {
970 mFgHandler.post(() -> onRebuildComplete(entries));
976 static private boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) {
977 if (info1 == null || info2 == null) {
980 if (info1.packageName == null || info2.packageName == null) {
983 return info1.packageName.equals(info2.packageName);
986 private ArrayList<ApplicationsState.AppEntry> removeDuplicateIgnoringUser(
987 ArrayList<ApplicationsState.AppEntry> entries)
989 int size = entries.size();
990 // returnList will not have more entries than entries
991 ArrayList<ApplicationsState.AppEntry> returnEntries = new
992 ArrayList<ApplicationsState.AppEntry>(size);
994 // assume appinfo of same package but different users are grouped together
995 PackageItemInfo lastInfo = null;
996 for (int i = 0; i < size; i++) {
997 AppEntry appEntry = entries.get(i);
998 PackageItemInfo info = appEntry.info;
999 if (!packageNameEquals(lastInfo, appEntry.info)) {
1000 returnEntries.add(appEntry);
1004 returnEntries.trimToSize();
1005 return returnEntries;
1009 public void onRebuildComplete(ArrayList<AppEntry> entries) {
1010 if (mFilterMode == FILTER_APPS_POWER_WHITELIST ||
1011 mFilterMode == FILTER_APPS_POWER_WHITELIST_ALL) {
1012 entries = removeDuplicateIgnoringUser(entries);
1014 mBaseEntries = entries;
1015 if (mBaseEntries != null) {
1016 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
1020 mSections = EMPTY_SECTIONS;
1021 mPositionToSectionIndex = null;
1024 notifyDataSetChanged();
1025 // Restore the last scroll position if the number of entries added so far is bigger than
1027 if (mLastIndex != -1 && getCount() > mLastIndex) {
1028 mManageApplications.mListView.setSelectionFromTop(mLastIndex, mLastTop);
1032 if (mSession.getAllApps().size() != 0
1033 && mManageApplications.mListContainer.getVisibility() != View.VISIBLE) {
1034 Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
1035 mManageApplications.mListContainer, true, true);
1037 if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
1038 // No enabled or disabled filters for usage access.
1042 mManageApplications.setHasDisabled(mState.haveDisabledApps());
1045 private void rebuildSections() {
1046 if (mEntries!= null && mManageApplications.mListView.isFastScrollEnabled()) {
1048 if (mIndex == null) {
1049 LocaleList locales = mContext.getResources().getConfiguration().getLocales();
1050 if (locales.size() == 0) {
1051 locales = new LocaleList(Locale.ENGLISH);
1053 AlphabeticIndex<Locale> index = new AlphabeticIndex<>(locales.get(0));
1054 int localeCount = locales.size();
1055 for (int i = 1; i < localeCount; i++) {
1056 index.addLabels(locales.get(i));
1058 // Ensure we always have some base English locale buckets
1059 index.addLabels(Locale.ENGLISH);
1060 mIndex = index.buildImmutableIndex();
1063 ArrayList<SectionInfo> sections = new ArrayList<>();
1065 int totalEntries = mEntries.size();
1066 mPositionToSectionIndex = new int[totalEntries];
1068 for (int pos = 0; pos < totalEntries; pos++) {
1069 String label = mEntries.get(pos).label;
1070 int secId = mIndex.getBucketIndex(TextUtils.isEmpty(label) ? "" : label);
1071 if (secId != lastSecId) {
1073 sections.add(new SectionInfo(mIndex.getBucket(secId).getLabel(), pos));
1075 mPositionToSectionIndex[pos] = sections.size() - 1;
1077 mSections = sections.toArray(EMPTY_SECTIONS);
1079 mSections = EMPTY_SECTIONS;
1080 mPositionToSectionIndex = null;
1084 private void updateLoading() {
1085 Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
1086 mManageApplications.mListContainer,
1087 mHasReceivedLoadEntries && mSession.getAllApps().size() != 0, false);
1090 ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
1091 ArrayList<ApplicationsState.AppEntry> origEntries) {
1092 if (prefix == null || prefix.length() == 0) {
1095 String prefixStr = ApplicationsState.normalize(prefix.toString());
1096 final String spacePrefixStr = " " + prefixStr;
1097 ArrayList<ApplicationsState.AppEntry> newEntries
1098 = new ArrayList<ApplicationsState.AppEntry>();
1099 for (int i = 0; i < origEntries.size(); i++) {
1100 ApplicationsState.AppEntry entry = origEntries.get(i);
1101 String nlabel = entry.getNormalizedLabel();
1102 if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
1103 newEntries.add(entry);
1111 public void onExtraInfoUpdated() {
1112 mHasReceivedBridgeCallback = true;
1117 public void onRunningStateChanged(boolean running) {
1118 mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running);
1122 public void onPackageListChanged() {
1127 public void onPackageIconChanged() {
1128 // We ensure icons are loaded when their item is displayed, so
1129 // don't care about icons loaded in the background.
1133 public void onLoadEntriesCompleted() {
1134 mHasReceivedLoadEntries = true;
1135 // We may have been skipping rebuilds until this came in, trigger one now.
1140 public void onPackageSizeChanged(String packageName) {
1141 for (int i = 0; i < mActive.size(); i++) {
1142 AppViewHolder holder = (AppViewHolder) mActive.get(i).getTag();
1143 if (holder == null) {
1146 ApplicationInfo info = holder.entry.info;
1150 if (holder.entry.info.packageName.equals(packageName)) {
1151 synchronized (holder.entry) {
1152 updateSummary(holder);
1154 if (holder.entry.info.packageName.equals(mManageApplications.mCurrentPkgName)
1155 && mLastSortMode == R.id.sort_order_size) {
1156 // We got the size information for the last app the
1157 // user viewed, and are sorting by size... they may
1158 // have cleared data, so we immediately want to resort
1159 // the list with the new size to reflect it to the user.
1168 public void onLauncherInfoChanged() {
1169 if (!mManageApplications.mShowSystem) {
1175 public void onAllSizesComputed() {
1176 if (mLastSortMode == R.id.sort_order_size) {
1181 public int getCount() {
1182 if (mEntries == null) {
1185 int extraViewAddition =
1186 (mExtraViewController != null && mExtraViewController.shouldShow()) ? 1 : 0;
1187 return mEntries.size() + extraViewAddition;
1190 public int getApplicationCount() {
1191 return mEntries != null ? mEntries.size() : 0;
1194 public Object getItem(int position) {
1195 if (position == mEntries.size()) {
1196 return mExtraViewController;
1198 return mEntries.get(position);
1201 public ApplicationsState.AppEntry getAppEntry(int position) {
1202 return mEntries.get(position);
1205 public long getItemId(int position) {
1206 if (position == mEntries.size()) {
1209 return mEntries.get(position).id;
1213 public boolean areAllItemsEnabled() {
1218 public boolean isEnabled(int position) {
1219 if (position == mEntries.size() && mExtraViewController != null &&
1220 mExtraViewController.shouldShow()) {
1224 if (mManageApplications.mListType != LIST_TYPE_HIGH_POWER) {
1227 ApplicationsState.AppEntry entry = mEntries.get(position);
1228 return !PowerWhitelistBackend.getInstance().isSysWhitelisted(entry.info.packageName);
1231 public View getView(int position, View convertView, ViewGroup parent) {
1232 // A ViewHolder keeps references to children views to avoid unnecessary calls
1233 // to findViewById() on each row.
1234 AppViewHolder holder = AppViewHolder.createOrRecycle(mManageApplications.mInflater,
1236 convertView = holder.rootView;
1238 // Handle the extra view if it is the last entry.
1239 if (mEntries != null && mExtraViewController != null && position == mEntries.size()) {
1240 mExtraViewController.setupView(holder);
1241 convertView.setEnabled(true);
1243 // Bind the data efficiently with the holder
1244 ApplicationsState.AppEntry entry = mEntries.get(position);
1245 synchronized (entry) {
1246 holder.entry = entry;
1247 if (entry.label != null) {
1248 holder.appName.setText(entry.label);
1250 mState.ensureIcon(entry);
1251 if (entry.icon != null) {
1252 holder.appIcon.setImageDrawable(entry.icon);
1254 updateSummary(holder);
1255 if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
1256 holder.disabled.setVisibility(View.VISIBLE);
1257 holder.disabled.setText(R.string.not_installed);
1258 } else if (!entry.info.enabled) {
1259 holder.disabled.setVisibility(View.VISIBLE);
1260 holder.disabled.setText(R.string.disabled);
1262 holder.disabled.setVisibility(View.GONE);
1265 convertView.setEnabled(isEnabled(position));
1268 mActive.remove(convertView);
1269 mActive.add(convertView);
1273 private void updateSummary(AppViewHolder holder) {
1274 switch (mManageApplications.mListType) {
1275 case LIST_TYPE_NOTIFICATION:
1276 if (holder.entry.extraInfo != null) {
1277 holder.summary.setText(InstalledAppDetails.getNotificationSummary(
1278 (AppRow) holder.entry.extraInfo, mContext));
1280 holder.summary.setText(null);
1284 case LIST_TYPE_USAGE_ACCESS:
1285 if (holder.entry.extraInfo != null) {
1286 holder.summary.setText((new UsageState((PermissionState) holder.entry
1287 .extraInfo)).isPermissible() ? R.string.switch_on_text :
1288 R.string.switch_off_text);
1290 holder.summary.setText(null);
1294 case LIST_TYPE_HIGH_POWER:
1295 holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry));
1298 case LIST_TYPE_OVERLAY:
1299 holder.summary.setText(DrawOverlayDetails.getSummary(mContext, holder.entry));
1302 case LIST_TYPE_WRITE_SETTINGS:
1303 holder.summary.setText(WriteSettingsDetails.getSummary(mContext,
1307 case LIST_TYPE_MANAGE_SOURCES:
1309 .setText(((InstallAppsState) holder.entry.extraInfo).getSummary());
1313 holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
1319 public Filter getFilter() {
1324 public void onMovedToScrapHeap(View view) {
1325 mActive.remove(view);
1329 public Object[] getSections() {
1334 public int getPositionForSection(int sectionIndex) {
1335 return mSections[sectionIndex].position;
1339 public int getSectionForPosition(int position) {
1340 return mPositionToSectionIndex[position];
1344 private static class SummaryProvider implements SummaryLoader.SummaryProvider {
1346 private final Context mContext;
1347 private final SummaryLoader mLoader;
1348 private ApplicationsState.Session mSession;
1350 private SummaryProvider(Context context, SummaryLoader loader) {
1356 public void setListening(boolean listening) {
1358 new InstalledAppCounter(mContext, ApplicationFeatureProvider.IGNORE_INSTALL_REASON,
1359 new PackageManagerWrapperImpl(mContext.getPackageManager())) {
1361 protected void onCountComplete(int num) {
1362 mLoader.setSummary(SummaryProvider.this,
1363 mContext.getString(R.string.apps_summary, num));
1367 protected List<UserInfo> getUsersToCount() {
1368 return mUm.getProfiles(UserHandle.myUserId());
1375 private static class SectionInfo {
1379 public SectionInfo(String label, int position) {
1381 this.position = position;
1385 public String toString() {
1390 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
1391 = new SummaryLoader.SummaryProviderFactory() {
1393 public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
1394 SummaryLoader summaryLoader) {
1395 return new SummaryProvider(activity, summaryLoader);