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.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ApplicationInfo;
23 import android.content.pm.PackageItemInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.UserInfo;
26 import android.icu.text.AlphabeticIndex;
27 import android.os.Bundle;
28 import android.os.Environment;
29 import android.os.Handler;
30 import android.os.LocaleList;
31 import android.os.UserHandle;
32 import android.os.UserManager;
33 import android.preference.PreferenceFrameLayout;
34 import android.text.TextUtils;
35 import android.util.Log;
36 import android.view.LayoutInflater;
37 import android.view.Menu;
38 import android.view.MenuInflater;
39 import android.view.MenuItem;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.widget.AbsListView;
43 import android.widget.AdapterView;
44 import android.widget.AdapterView.OnItemClickListener;
45 import android.widget.AdapterView.OnItemSelectedListener;
46 import android.widget.ArrayAdapter;
47 import android.widget.BaseAdapter;
48 import android.widget.Filter;
49 import android.widget.Filterable;
50 import android.widget.FrameLayout;
51 import android.widget.ListView;
52 import android.widget.SectionIndexer;
53 import android.widget.Spinner;
55 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
56 import com.android.settings.AppHeader;
57 import com.android.settings.R;
58 import com.android.settings.Settings.AllApplicationsActivity;
59 import com.android.settings.Settings.GamesStorageActivity;
60 import com.android.settings.Settings.HighPowerApplicationsActivity;
61 import com.android.settings.Settings.ManageExternalSourcesActivity;
62 import com.android.settings.Settings.NotificationAppListActivity;
63 import com.android.settings.Settings.OverlaySettingsActivity;
64 import com.android.settings.Settings.StorageUseActivity;
65 import com.android.settings.Settings.UsageAccessSettingsActivity;
66 import com.android.settings.Settings.WriteSettingsActivity;
67 import com.android.settings.SettingsActivity;
68 import com.android.settings.Utils;
69 import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
70 import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState;
71 import com.android.settings.applications.AppStateUsageBridge.UsageState;
72 import com.android.settings.core.InstrumentedPreferenceFragment;
73 import com.android.settings.dashboard.SummaryLoader;
74 import com.android.settings.fuelgauge.HighPowerDetail;
75 import com.android.settings.fuelgauge.PowerWhitelistBackend;
76 import com.android.settings.notification.AppNotificationSettings;
77 import com.android.settings.notification.ConfigureNotificationSettings;
78 import com.android.settings.notification.NotificationBackend;
79 import com.android.settings.notification.NotificationBackend.AppRow;
80 import com.android.settings.overlay.FeatureFactory;
81 import com.android.settingslib.HelpUtils;
82 import com.android.settingslib.applications.ApplicationsState;
83 import com.android.settingslib.applications.ApplicationsState.AppEntry;
84 import com.android.settingslib.applications.ApplicationsState.AppFilter;
85 import com.android.settingslib.applications.ApplicationsState.CompoundFilter;
86 import com.android.settingslib.applications.ApplicationsState.VolumeFilter;
88 import java.util.ArrayList;
89 import java.util.Collections;
90 import java.util.Comparator;
91 import java.util.List;
92 import java.util.Locale;
95 * Activity to pick an application that will be used to display installation information and
96 * options to uninstall/delete user data for system applications. This activity
97 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
100 public class ManageApplications extends InstrumentedPreferenceFragment
101 implements OnItemClickListener, OnItemSelectedListener {
103 static final String TAG = "ManageApplications";
104 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
107 public static final String EXTRA_CLASSNAME = "classname";
108 // Used for storage only.
109 public static final String EXTRA_VOLUME_UUID = "volumeUuid";
110 public static final String EXTRA_VOLUME_NAME = "volumeName";
112 private static final String EXTRA_SORT_ORDER = "sortOrder";
113 private static final String EXTRA_SHOW_SYSTEM = "showSystem";
114 private static final String EXTRA_HAS_ENTRIES = "hasEntries";
115 private static final String EXTRA_HAS_BRIDGE = "hasBridge";
117 // attributes used as keys when passing values to InstalledAppDetails activity
118 public static final String APP_CHG = "chg";
120 // constant value that can be used to check return code from sub activity.
121 private static final int INSTALLED_APP_DETAILS = 1;
122 private static final int ADVANCED_SETTINGS = 2;
124 public static final int SIZE_TOTAL = 0;
125 public static final int SIZE_INTERNAL = 1;
126 public static final int SIZE_EXTERNAL = 2;
128 // Filter options used for displayed list of applications
129 // The order which they appear is the order they will show when spinner is present.
130 public static final int FILTER_APPS_POWER_WHITELIST = 0;
131 public static final int FILTER_APPS_POWER_WHITELIST_ALL = 1;
132 public static final int FILTER_APPS_ALL = 2;
133 public static final int FILTER_APPS_ENABLED = 3;
134 public static final int FILTER_APPS_DISABLED = 4;
135 public static final int FILTER_APPS_BLOCKED = 5;
136 public static final int FILTER_APPS_PERSONAL = 6;
137 public static final int FILTER_APPS_WORK = 7;
138 public static final int FILTER_APPS_USAGE_ACCESS = 8;
139 public static final int FILTER_APPS_WITH_OVERLAY = 9;
140 public static final int FILTER_APPS_WRITE_SETTINGS = 10;
141 public static final int FILTER_APPS_INSTALL_SOURCES = 12;
143 // This is the string labels for the filter modes above, the order must be kept in sync.
144 public static final int[] FILTER_LABELS = new int[]{
145 R.string.high_power_filter_on, // High power whitelist, on
146 R.string.filter_all_apps, // Without disabled until used
147 R.string.filter_all_apps, // All apps
148 R.string.filter_enabled_apps, // Enabled
149 R.string.filter_apps_disabled, // Disabled
150 R.string.filter_notif_blocked_apps, // Blocked Notifications
151 R.string.filter_personal_apps, // Personal
152 R.string.filter_work_apps, // Work
153 R.string.filter_with_domain_urls_apps, // Domain URLs
154 R.string.filter_all_apps, // Usage access screen, never displayed
155 R.string.filter_overlay_apps, // Apps with overlay permission
156 R.string.filter_write_settings_apps, // Apps that can write system settings
157 R.string.filter_install_sources_apps, // Apps that are trusted sources of apks
159 // This is the actual mapping to filters from FILTER_ constants above, the order must
161 public static final AppFilter[] FILTERS = new AppFilter[]{
162 new CompoundFilter(AppStatePowerBridge.FILTER_POWER_WHITELISTED,
163 ApplicationsState.FILTER_ALL_ENABLED), // High power whitelist, on
164 new CompoundFilter(ApplicationsState.FILTER_WITHOUT_DISABLED_UNTIL_USED,
165 ApplicationsState.FILTER_ALL_ENABLED), // Without disabled until used
166 ApplicationsState.FILTER_EVERYTHING, // All apps
167 ApplicationsState.FILTER_ALL_ENABLED, // Enabled
168 ApplicationsState.FILTER_DISABLED, // Disabled
169 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED, // Blocked Notifications
170 ApplicationsState.FILTER_PERSONAL, // Personal
171 ApplicationsState.FILTER_WORK, // Work
172 ApplicationsState.FILTER_WITH_DOMAIN_URLS, // Apps with Domain URLs
173 AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
174 AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW, // Apps that can draw overlays
175 AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS, // Apps that can write system settings
176 AppStateInstallAppsBridge.FILTER_APP_SOURCES,
180 private int mSortOrder = R.id.sort_order_alpha;
182 // whether showing system apps.
183 private boolean mShowSystem;
185 private ApplicationsState mApplicationsState;
187 public int mListType;
190 public ApplicationsAdapter mApplications;
192 private View mLoadingContainer;
194 private View mListContainer;
196 // ListView used to display list
197 private ListView mListView;
199 // Size resource used for packages whose size computation failed for some reason
200 CharSequence mInvalidSizeStr;
202 // layout inflater object used to inflate views
203 private LayoutInflater mInflater;
205 private String mCurrentPkgName;
206 private int mCurrentUid;
207 private boolean mFinishAfterDialog;
209 private Menu mOptionsMenu;
211 public static final int LIST_TYPE_MAIN = 0;
212 public static final int LIST_TYPE_NOTIFICATION = 1;
213 public static final int LIST_TYPE_STORAGE = 3;
214 public static final int LIST_TYPE_USAGE_ACCESS = 4;
215 public static final int LIST_TYPE_HIGH_POWER = 5;
216 public static final int LIST_TYPE_OVERLAY = 6;
217 public static final int LIST_TYPE_WRITE_SETTINGS = 7;
218 public static final int LIST_TYPE_MANAGE_SOURCES = 8;
219 public static final int LIST_TYPE_GAMES = 9;
221 private View mRootView;
223 private View mSpinnerHeader;
224 private Spinner mFilterSpinner;
225 private FilterSpinnerAdapter mFilterAdapter;
226 private NotificationBackend mNotifBackend;
227 private ResetAppsHelper mResetAppsHelper;
228 private String mVolumeUuid;
229 private String mVolumeName;
232 public void onCreate(Bundle savedInstanceState) {
233 super.onCreate(savedInstanceState);
234 setHasOptionsMenu(true);
235 mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
237 Intent intent = getActivity().getIntent();
238 Bundle args = getArguments();
239 String className = args != null ? args.getString(EXTRA_CLASSNAME) : null;
240 if (className == null) {
241 className = intent.getComponent().getClassName();
243 if (className.equals(AllApplicationsActivity.class.getName())) {
245 } else if (className.equals(NotificationAppListActivity.class.getName())
246 || this instanceof NotificationApps) {
247 mListType = LIST_TYPE_NOTIFICATION;
248 mNotifBackend = new NotificationBackend();
249 } else if (className.equals(StorageUseActivity.class.getName())) {
250 if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) {
251 mVolumeUuid = args.getString(EXTRA_VOLUME_UUID);
252 mVolumeName = args.getString(EXTRA_VOLUME_NAME);
253 mListType = LIST_TYPE_STORAGE;
255 // No volume selected, display a normal list, sorted by size.
256 mListType = LIST_TYPE_MAIN;
258 mSortOrder = R.id.sort_order_size;
259 } else if (className.equals(UsageAccessSettingsActivity.class.getName())) {
260 mListType = LIST_TYPE_USAGE_ACCESS;
261 } else if (className.equals(HighPowerApplicationsActivity.class.getName())) {
262 mListType = LIST_TYPE_HIGH_POWER;
263 // Default to showing system.
265 } else if (className.equals(OverlaySettingsActivity.class.getName())) {
266 mListType = LIST_TYPE_OVERLAY;
267 } else if (className.equals(WriteSettingsActivity.class.getName())) {
268 mListType = LIST_TYPE_WRITE_SETTINGS;
269 } else if (className.equals(ManageExternalSourcesActivity.class.getName())) {
270 mListType = LIST_TYPE_MANAGE_SOURCES;
271 } else if (className.equals(GamesStorageActivity.class.getName())) {
272 mListType = LIST_TYPE_GAMES;
273 mSortOrder = R.id.sort_order_size;
275 mListType = LIST_TYPE_MAIN;
277 mFilter = getDefaultFilter();
279 if (savedInstanceState != null) {
280 mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
281 mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
284 mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
286 mResetAppsHelper = new ResetAppsHelper(getActivity());
291 public View onCreateView(LayoutInflater inflater, ViewGroup container,
292 Bundle savedInstanceState) {
293 // initialize the inflater
294 mInflater = inflater;
296 mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
297 mLoadingContainer = mRootView.findViewById(R.id.loading_container);
298 mLoadingContainer.setVisibility(View.VISIBLE);
299 mListContainer = mRootView.findViewById(R.id.list_container);
300 if (mListContainer != null) {
301 // Create adapter and list view here
302 View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
303 ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
304 if (emptyView != null) {
305 lv.setEmptyView(emptyView);
307 lv.setOnItemClickListener(this);
308 lv.setSaveEnabled(true);
309 lv.setItemsCanFocus(true);
310 lv.setTextFilterEnabled(true);
312 mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
313 if (savedInstanceState != null) {
314 mApplications.mHasReceivedLoadEntries =
315 savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false);
316 mApplications.mHasReceivedBridgeCallback =
317 savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false);
319 mListView.setAdapter(mApplications);
320 mListView.setRecyclerListener(mApplications);
321 mListView.setFastScrollEnabled(isFastScrollEnabled());
323 Utils.prepareCustomPreferencesList(container, mRootView, mListView, false);
326 // We have to do this now because PreferenceFrameLayout looks at it
327 // only when the view is added.
328 if (container instanceof PreferenceFrameLayout) {
329 ((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true;
334 mResetAppsHelper.onRestoreInstanceState(savedInstanceState);
339 private void createHeader() {
340 Activity activity = getActivity();
341 FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
342 mSpinnerHeader = activity.getLayoutInflater()
343 .inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
344 mFilterSpinner = (Spinner) mSpinnerHeader.findViewById(R.id.filter_spinner);
345 mFilterAdapter = new FilterSpinnerAdapter(this);
346 mFilterSpinner.setAdapter(mFilterAdapter);
347 mFilterSpinner.setOnItemSelectedListener(this);
348 pinnedHeader.addView(mSpinnerHeader, 0);
350 mFilterAdapter.enableFilter(getDefaultFilter());
351 if (mListType == LIST_TYPE_MAIN) {
352 if (UserManager.get(getActivity()).getUserProfiles().size() > 1) {
353 mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL);
354 mFilterAdapter.enableFilter(FILTER_APPS_WORK);
357 if (mListType == LIST_TYPE_NOTIFICATION) {
358 mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED);
360 if (mListType == LIST_TYPE_HIGH_POWER) {
361 mFilterAdapter.enableFilter(FILTER_APPS_POWER_WHITELIST_ALL);
363 if (mListType == LIST_TYPE_STORAGE) {
364 mApplications.setOverrideFilter(new VolumeFilter(mVolumeUuid));
366 if (mListType == LIST_TYPE_GAMES) {
367 mApplications.setOverrideFilter(ApplicationsState.FILTER_GAMES);
372 public void onViewCreated(View view, Bundle savedInstanceState) {
373 super.onViewCreated(view, savedInstanceState);
375 if (mListType == LIST_TYPE_STORAGE) {
376 final Activity activity = getActivity();
377 final boolean isNewIAEnabled = FeatureFactory.getFactory(activity)
378 .getDashboardFeatureProvider(activity)
380 if (!isNewIAEnabled) {
381 FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
382 AppHeader.createAppHeader(getActivity(), null, mVolumeName, null, -1, pinnedHeader);
387 private int getDefaultFilter() {
389 case LIST_TYPE_USAGE_ACCESS:
390 return FILTER_APPS_USAGE_ACCESS;
391 case LIST_TYPE_HIGH_POWER:
392 return FILTER_APPS_POWER_WHITELIST;
393 case LIST_TYPE_OVERLAY:
394 return FILTER_APPS_WITH_OVERLAY;
395 case LIST_TYPE_WRITE_SETTINGS:
396 return FILTER_APPS_WRITE_SETTINGS;
397 case LIST_TYPE_MANAGE_SOURCES:
398 return FILTER_APPS_INSTALL_SOURCES;
400 return FILTER_APPS_ALL;
404 private boolean isFastScrollEnabled() {
407 case LIST_TYPE_NOTIFICATION:
408 case LIST_TYPE_STORAGE:
409 case LIST_TYPE_GAMES:
410 return mSortOrder == R.id.sort_order_alpha;
417 public int getMetricsCategory() {
420 return MetricsEvent.MANAGE_APPLICATIONS;
421 case LIST_TYPE_NOTIFICATION:
422 return MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS;
423 case LIST_TYPE_STORAGE:
424 case LIST_TYPE_GAMES:
425 return MetricsEvent.APPLICATIONS_STORAGE_APPS;
426 case LIST_TYPE_USAGE_ACCESS:
427 return MetricsEvent.USAGE_ACCESS;
428 case LIST_TYPE_HIGH_POWER:
429 return MetricsEvent.APPLICATIONS_HIGH_POWER_APPS;
430 case LIST_TYPE_OVERLAY:
431 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
432 case LIST_TYPE_WRITE_SETTINGS:
433 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
434 case LIST_TYPE_MANAGE_SOURCES:
435 return MetricsEvent.MANAGE_EXTERNAL_SOURCES;
437 return MetricsEvent.VIEW_UNKNOWN;
442 public void onResume() {
446 if (mApplications != null) {
447 mApplications.resume(mSortOrder);
448 mApplications.updateLoading();
453 public void onSaveInstanceState(Bundle outState) {
454 super.onSaveInstanceState(outState);
455 mResetAppsHelper.onSaveInstanceState(outState);
456 outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
457 outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
458 outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries);
459 outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback);
463 public void onPause() {
465 if (mApplications != null) {
466 mApplications.pause();
471 public void onStop() {
473 mResetAppsHelper.stop();
477 public void onDestroyView() {
478 super.onDestroyView();
480 if (mApplications != null) {
481 mApplications.release();
487 public void onActivityResult(int requestCode, int resultCode, Intent data) {
488 if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
489 if (mListType == LIST_TYPE_NOTIFICATION) {
490 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
491 } else if (mListType == LIST_TYPE_HIGH_POWER || mListType == LIST_TYPE_OVERLAY
492 || mListType == LIST_TYPE_WRITE_SETTINGS) {
493 if (mFinishAfterDialog) {
494 getActivity().onBackPressed();
496 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
499 mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid));
504 // utility method used to start sub activity
505 private void startApplicationDetailsActivity() {
507 case LIST_TYPE_NOTIFICATION:
508 startAppInfoFragment(AppNotificationSettings.class,
509 R.string.app_notifications_title);
511 case LIST_TYPE_USAGE_ACCESS:
512 startAppInfoFragment(UsageAccessDetails.class, R.string.usage_access);
514 case LIST_TYPE_STORAGE:
515 startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings);
517 case LIST_TYPE_HIGH_POWER:
518 HighPowerDetail.show(this, mCurrentPkgName, INSTALLED_APP_DETAILS,
521 case LIST_TYPE_OVERLAY:
522 startAppInfoFragment(DrawOverlayDetails.class, R.string.overlay_settings);
524 case LIST_TYPE_WRITE_SETTINGS:
525 startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings);
527 case LIST_TYPE_MANAGE_SOURCES:
528 startAppInfoFragment(ExternalSourcesDetails.class, R.string.install_other_apps);
529 case LIST_TYPE_GAMES:
530 startAppInfoFragment(AppStorageSettings.class, R.string.game_storage_settings);
532 // TODO: Figure out if there is a way where we can spin up the profile's settings
533 // process ahead of time, to avoid a long load of data when user clicks on a managed app.
534 // Maybe when they load the list of apps that contains managed profile apps.
536 startAppInfoFragment(InstalledAppDetails.class, R.string.application_info_label);
541 private void startAppInfoFragment(Class<?> fragment, int titleRes) {
542 AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this,
543 INSTALLED_APP_DETAILS);
547 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
548 HelpUtils.prepareHelpMenuItem(getActivity(), menu, mListType == LIST_TYPE_MAIN
549 ? R.string.help_uri_apps : R.string.help_uri_notifications, getClass().getName());
551 inflater.inflate(R.menu.manage_apps, menu);
556 public void onPrepareOptionsMenu(Menu menu) {
561 public void onDestroyOptionsMenu() {
565 void updateOptionsMenu() {
566 if (mOptionsMenu == null) {
569 final Context context = getActivity();
570 if (FeatureFactory.getFactory(context).getDashboardFeatureProvider(context).isEnabled()) {
571 mOptionsMenu.findItem(R.id.advanced).setVisible(false);
573 mOptionsMenu.findItem(R.id.advanced).setVisible(
574 mListType == LIST_TYPE_MAIN || mListType == LIST_TYPE_NOTIFICATION);
577 mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE
578 && mSortOrder != R.id.sort_order_alpha);
579 mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE
580 && mSortOrder != R.id.sort_order_size);
582 mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem
583 && mListType != LIST_TYPE_HIGH_POWER);
584 mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem
585 && mListType != LIST_TYPE_HIGH_POWER);
589 public boolean onOptionsItemSelected(MenuItem item) {
590 int menuId = item.getItemId();
591 switch (item.getItemId()) {
592 case R.id.sort_order_alpha:
593 case R.id.sort_order_size:
595 mListView.setFastScrollEnabled(isFastScrollEnabled());
596 if (mApplications != null) {
597 mApplications.rebuild(mSortOrder);
600 case R.id.show_system:
601 case R.id.hide_system:
602 mShowSystem = !mShowSystem;
603 mApplications.rebuild(false);
605 case R.id.reset_app_preferences:
606 mResetAppsHelper.buildResetDialog();
609 if (mListType == LIST_TYPE_NOTIFICATION) {
610 ((SettingsActivity) getActivity()).startPreferencePanel(
611 ConfigureNotificationSettings.class.getName(), null,
612 R.string.configure_notification_settings, null, this, ADVANCED_SETTINGS);
614 ((SettingsActivity) getActivity()).startPreferencePanel(
615 AdvancedAppSettings.class.getName(), null, R.string.configure_apps,
616 null, this, ADVANCED_SETTINGS);
620 // Handle the home button
628 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
629 if (mApplications != null && mApplications.getCount() > position) {
630 ApplicationsState.AppEntry entry = mApplications.getAppEntry(position);
631 mCurrentPkgName = entry.info.packageName;
632 mCurrentUid = entry.info.uid;
633 startApplicationDetailsActivity();
638 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
639 mFilter = mFilterAdapter.getFilter(position);
640 mApplications.setFilter(mFilter);
641 if (DEBUG) Log.d(TAG, "Selecting filter " + mFilter);
645 public void onNothingSelected(AdapterView<?> parent) {
648 public void updateView() {
650 final Activity host = getActivity();
652 host.invalidateOptionsMenu();
656 public void setHasDisabled(boolean hasDisabledApps) {
657 if (mListType != LIST_TYPE_MAIN) {
660 mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps);
661 mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps);
664 static class FilterSpinnerAdapter extends ArrayAdapter<CharSequence> {
666 private final ManageApplications mManageApplications;
668 // Use ArrayAdapter for view logic, but have our own list for managing
669 // the options available.
670 private final ArrayList<Integer> mFilterOptions = new ArrayList<>();
672 public FilterSpinnerAdapter(ManageApplications manageApplications) {
673 super(manageApplications.mFilterSpinner.getContext(), R.layout.filter_spinner_item);
674 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
675 mManageApplications = manageApplications;
678 public int getFilter(int position) {
679 return mFilterOptions.get(position);
682 public void setFilterEnabled(int filter, boolean enabled) {
684 enableFilter(filter);
686 disableFilter(filter);
690 public void enableFilter(int filter) {
691 if (mFilterOptions.contains(filter)) return;
692 if (DEBUG) Log.d(TAG, "Enabling filter " + filter);
693 mFilterOptions.add(filter);
694 Collections.sort(mFilterOptions);
695 mManageApplications.mSpinnerHeader.setVisibility(
696 mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
697 notifyDataSetChanged();
698 if (mFilterOptions.size() == 1) {
699 if (DEBUG) Log.d(TAG, "Auto selecting filter " + filter);
700 mManageApplications.mFilterSpinner.setSelection(0);
701 mManageApplications.onItemSelected(null, null, 0, 0);
705 public void disableFilter(int filter) {
706 if (!mFilterOptions.remove((Integer) filter)) {
709 if (DEBUG) Log.d(TAG, "Disabling filter " + filter);
710 Collections.sort(mFilterOptions);
711 mManageApplications.mSpinnerHeader.setVisibility(
712 mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
713 notifyDataSetChanged();
714 if (mManageApplications.mFilter == filter) {
715 if (mFilterOptions.size() > 0) {
716 if (DEBUG) Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0));
717 mManageApplications.mFilterSpinner.setSelection(0);
718 mManageApplications.onItemSelected(null, null, 0, 0);
724 public int getCount() {
725 return mFilterOptions.size();
729 public CharSequence getItem(int position) {
730 return getFilterString(mFilterOptions.get(position));
733 private CharSequence getFilterString(int filter) {
734 return mManageApplications.getString(FILTER_LABELS[filter]);
740 * Custom adapter implementation for the ListView
741 * This adapter maintains a map for each displayed application and its properties
742 * An index value on each AppInfo object indicates the correct position or index
743 * in the list. If the list gets updated dynamically when the user is viewing the list of
744 * applications, we need to return the correct index of position. This is done by mapping
745 * the getId methods via the package name into the internal maps and indices.
746 * The order of applications in the list is mirrored in mAppLocalList
748 static class ApplicationsAdapter extends BaseAdapter implements Filterable,
749 ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
750 AbsListView.RecyclerListener, SectionIndexer {
751 private static final SectionInfo[] EMPTY_SECTIONS = new SectionInfo[0];
753 private final ApplicationsState mState;
754 private final ApplicationsState.Session mSession;
755 private final ManageApplications mManageApplications;
756 private final Context mContext;
757 private final ArrayList<View> mActive = new ArrayList<View>();
758 private final AppStateBaseBridge mExtraInfoBridge;
759 private final Handler mBgHandler;
760 private final Handler mFgHandler;
761 private int mFilterMode;
762 private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
763 private ArrayList<ApplicationsState.AppEntry> mEntries;
764 private boolean mResumed;
765 private int mLastSortMode = -1;
766 private int mWhichSize = SIZE_TOTAL;
767 CharSequence mCurFilterPrefix;
768 private PackageManager mPm;
769 private AppFilter mOverrideFilter;
770 private boolean mHasReceivedLoadEntries;
771 private boolean mHasReceivedBridgeCallback;
773 // These two variables are used to remember and restore the last scroll position when this
774 // fragment is paused. We need this special handling because app entries are added gradually
775 // when we rebuild the list after the user made some changes, like uninstalling an app.
776 private int mLastIndex = -1;
777 private int mLastTop;
779 private AlphabeticIndex.ImmutableIndex<Locale> mIndex;
780 private SectionInfo[] mSections = EMPTY_SECTIONS;
781 private int[] mPositionToSectionIndex;
783 private Filter mFilter = new Filter() {
785 protected FilterResults performFiltering(CharSequence constraint) {
786 ArrayList<ApplicationsState.AppEntry> entries
787 = applyPrefixFilter(constraint, mBaseEntries);
788 FilterResults fr = new FilterResults();
790 fr.count = entries.size();
795 @SuppressWarnings("unchecked")
796 protected void publishResults(CharSequence constraint, FilterResults results) {
797 mCurFilterPrefix = constraint;
798 mEntries = (ArrayList<ApplicationsState.AppEntry>) results.values;
800 notifyDataSetChanged();
804 public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
807 mFgHandler = new Handler();
808 mBgHandler = new Handler(mState.getBackgroundLooper());
809 mSession = state.newSession(this);
810 mManageApplications = manageApplications;
811 mContext = manageApplications.getActivity();
812 mPm = mContext.getPackageManager();
813 mFilterMode = filterMode;
814 if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
815 mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this,
816 manageApplications.mNotifBackend);
817 } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
818 mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
819 } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) {
820 mExtraInfoBridge = new AppStatePowerBridge(mState, this);
821 } else if (mManageApplications.mListType == LIST_TYPE_OVERLAY) {
822 mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this);
823 } else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) {
824 mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this);
825 } else if (mManageApplications.mListType == LIST_TYPE_MANAGE_SOURCES) {
826 mExtraInfoBridge = new AppStateInstallAppsBridge(mContext, mState, this);
828 mExtraInfoBridge = null;
832 public void setOverrideFilter(AppFilter overrideFilter) {
833 mOverrideFilter = overrideFilter;
837 public void setFilter(int filter) {
838 mFilterMode = filter;
842 public void resume(int sort) {
843 if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed);
847 mLastSortMode = sort;
848 if (mExtraInfoBridge != null) {
849 mExtraInfoBridge.resume();
857 public void pause() {
861 if (mExtraInfoBridge != null) {
862 mExtraInfoBridge.pause();
865 // Record the current scroll position before pausing.
866 mLastIndex = mManageApplications.mListView.getFirstVisiblePosition();
867 View v = mManageApplications.mListView.getChildAt(0);
868 mLastTop = (v == null) ? 0 : (v.getTop() - mManageApplications.mListView.getPaddingTop());
871 public void release() {
873 if (mExtraInfoBridge != null) {
874 mExtraInfoBridge.release();
878 public void rebuild(int sort) {
879 if (sort == mLastSortMode) {
882 mLastSortMode = sort;
886 public void rebuild(boolean eraseold) {
887 if (!mHasReceivedLoadEntries
888 || (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) {
889 // Don't rebuild the list until all the app entries are loaded.
892 if (DEBUG) Log.i(TAG, "Rebuilding app list...");
893 ApplicationsState.AppFilter filterObj;
894 Comparator<AppEntry> comparatorObj;
895 boolean emulated = Environment.isExternalStorageEmulated();
897 mWhichSize = SIZE_TOTAL;
899 mWhichSize = SIZE_INTERNAL;
901 filterObj = FILTERS[mFilterMode];
902 if (mOverrideFilter != null) {
903 filterObj = mOverrideFilter;
905 if (!mManageApplications.mShowSystem) {
906 filterObj = new CompoundFilter(filterObj,
907 ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
909 switch (mLastSortMode) {
910 case R.id.sort_order_size:
911 switch (mWhichSize) {
913 comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
916 comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
919 comparatorObj = ApplicationsState.SIZE_COMPARATOR;
924 comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
927 filterObj = new CompoundFilter(filterObj, ApplicationsState.FILTER_NOT_HIDE);
929 AppFilter finalFilterObj = filterObj;
930 mBgHandler.post(() -> {
931 final ArrayList<AppEntry> entries = mSession.rebuild(finalFilterObj,
932 comparatorObj, false);
933 if (entries != null) {
934 mFgHandler.post(() -> onRebuildComplete(entries));
940 static private boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) {
941 if (info1 == null || info2 == null) {
944 if (info1.packageName == null || info2.packageName == null) {
947 return info1.packageName.equals(info2.packageName);
950 private ArrayList<ApplicationsState.AppEntry> removeDuplicateIgnoringUser(
951 ArrayList<ApplicationsState.AppEntry> entries)
953 int size = entries.size();
954 // returnList will not have more entries than entries
955 ArrayList<ApplicationsState.AppEntry> returnEntries = new
956 ArrayList<ApplicationsState.AppEntry>(size);
958 // assume appinfo of same package but different users are grouped together
959 PackageItemInfo lastInfo = null;
960 for (int i = 0; i < size; i++) {
961 AppEntry appEntry = entries.get(i);
962 PackageItemInfo info = appEntry.info;
963 if (!packageNameEquals(lastInfo, appEntry.info)) {
964 returnEntries.add(appEntry);
968 returnEntries.trimToSize();
969 return returnEntries;
973 public void onRebuildComplete(ArrayList<AppEntry> entries) {
974 if (mFilterMode == FILTER_APPS_POWER_WHITELIST ||
975 mFilterMode == FILTER_APPS_POWER_WHITELIST_ALL) {
976 entries = removeDuplicateIgnoringUser(entries);
978 mBaseEntries = entries;
979 if (mBaseEntries != null) {
980 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
984 mSections = EMPTY_SECTIONS;
985 mPositionToSectionIndex = null;
988 notifyDataSetChanged();
989 // Restore the last scroll position if the number of entries added so far is bigger than
991 if (mLastIndex != -1 && getCount() > mLastIndex) {
992 mManageApplications.mListView.setSelectionFromTop(mLastIndex, mLastTop);
996 if (mSession.getAllApps().size() != 0
997 && mManageApplications.mListContainer.getVisibility() != View.VISIBLE) {
998 Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
999 mManageApplications.mListContainer, true, true);
1001 if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
1002 // No enabled or disabled filters for usage access.
1006 mManageApplications.setHasDisabled(mState.haveDisabledApps());
1009 private void rebuildSections() {
1010 if (mEntries!= null && mManageApplications.mListView.isFastScrollEnabled()) {
1012 if (mIndex == null) {
1013 LocaleList locales = mContext.getResources().getConfiguration().getLocales();
1014 if (locales.size() == 0) {
1015 locales = new LocaleList(Locale.ENGLISH);
1017 AlphabeticIndex<Locale> index = new AlphabeticIndex<>(locales.get(0));
1018 int localeCount = locales.size();
1019 for (int i = 1; i < localeCount; i++) {
1020 index.addLabels(locales.get(i));
1022 // Ensure we always have some base English locale buckets
1023 index.addLabels(Locale.ENGLISH);
1024 mIndex = index.buildImmutableIndex();
1027 ArrayList<SectionInfo> sections = new ArrayList<>();
1029 int totalEntries = mEntries.size();
1030 mPositionToSectionIndex = new int[totalEntries];
1032 for (int pos = 0; pos < totalEntries; pos++) {
1033 String label = mEntries.get(pos).label;
1034 int secId = mIndex.getBucketIndex(TextUtils.isEmpty(label) ? "" : label);
1035 if (secId != lastSecId) {
1037 sections.add(new SectionInfo(mIndex.getBucket(secId).getLabel(), pos));
1039 mPositionToSectionIndex[pos] = sections.size() - 1;
1041 mSections = sections.toArray(EMPTY_SECTIONS);
1043 mSections = EMPTY_SECTIONS;
1044 mPositionToSectionIndex = null;
1048 private void updateLoading() {
1049 Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
1050 mManageApplications.mListContainer,
1051 mHasReceivedLoadEntries && mSession.getAllApps().size() != 0, false);
1054 ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
1055 ArrayList<ApplicationsState.AppEntry> origEntries) {
1056 if (prefix == null || prefix.length() == 0) {
1059 String prefixStr = ApplicationsState.normalize(prefix.toString());
1060 final String spacePrefixStr = " " + prefixStr;
1061 ArrayList<ApplicationsState.AppEntry> newEntries
1062 = new ArrayList<ApplicationsState.AppEntry>();
1063 for (int i = 0; i < origEntries.size(); i++) {
1064 ApplicationsState.AppEntry entry = origEntries.get(i);
1065 String nlabel = entry.getNormalizedLabel();
1066 if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
1067 newEntries.add(entry);
1075 public void onExtraInfoUpdated() {
1076 mHasReceivedBridgeCallback = true;
1081 public void onRunningStateChanged(boolean running) {
1082 mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running);
1086 public void onPackageListChanged() {
1091 public void onPackageIconChanged() {
1092 // We ensure icons are loaded when their item is displayed, so
1093 // don't care about icons loaded in the background.
1097 public void onLoadEntriesCompleted() {
1098 mHasReceivedLoadEntries = true;
1099 // We may have been skipping rebuilds until this came in, trigger one now.
1104 public void onPackageSizeChanged(String packageName) {
1105 for (int i = 0; i < mActive.size(); i++) {
1106 AppViewHolder holder = (AppViewHolder) mActive.get(i).getTag();
1107 if (holder.entry.info.packageName.equals(packageName)) {
1108 synchronized (holder.entry) {
1109 updateSummary(holder);
1111 if (holder.entry.info.packageName.equals(mManageApplications.mCurrentPkgName)
1112 && mLastSortMode == R.id.sort_order_size) {
1113 // We got the size information for the last app the
1114 // user viewed, and are sorting by size... they may
1115 // have cleared data, so we immediately want to resort
1116 // the list with the new size to reflect it to the user.
1125 public void onLauncherInfoChanged() {
1126 if (!mManageApplications.mShowSystem) {
1132 public void onAllSizesComputed() {
1133 if (mLastSortMode == R.id.sort_order_size) {
1138 public int getCount() {
1139 return mEntries != null ? mEntries.size() : 0;
1142 public Object getItem(int position) {
1143 return mEntries.get(position);
1146 public ApplicationsState.AppEntry getAppEntry(int position) {
1147 return mEntries.get(position);
1150 public long getItemId(int position) {
1151 return mEntries.get(position).id;
1155 public boolean areAllItemsEnabled() {
1160 public boolean isEnabled(int position) {
1161 if (mManageApplications.mListType != LIST_TYPE_HIGH_POWER) {
1164 ApplicationsState.AppEntry entry = mEntries.get(position);
1165 return !PowerWhitelistBackend.getInstance().isSysWhitelisted(entry.info.packageName);
1168 public View getView(int position, View convertView, ViewGroup parent) {
1169 // A ViewHolder keeps references to children views to avoid unnecessary calls
1170 // to findViewById() on each row.
1171 AppViewHolder holder = AppViewHolder.createOrRecycle(mManageApplications.mInflater,
1173 convertView = holder.rootView;
1175 // Bind the data efficiently with the holder
1176 ApplicationsState.AppEntry entry = mEntries.get(position);
1177 synchronized (entry) {
1178 holder.entry = entry;
1179 if (entry.label != null) {
1180 holder.appName.setText(entry.label);
1182 mState.ensureIcon(entry);
1183 if (entry.icon != null) {
1184 holder.appIcon.setImageDrawable(entry.icon);
1186 updateSummary(holder);
1187 if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
1188 holder.disabled.setVisibility(View.VISIBLE);
1189 holder.disabled.setText(R.string.not_installed);
1190 } else if (!entry.info.enabled) {
1191 holder.disabled.setVisibility(View.VISIBLE);
1192 holder.disabled.setText(R.string.disabled);
1194 holder.disabled.setVisibility(View.GONE);
1197 mActive.remove(convertView);
1198 mActive.add(convertView);
1199 convertView.setEnabled(isEnabled(position));
1203 private void updateSummary(AppViewHolder holder) {
1204 switch (mManageApplications.mListType) {
1205 case LIST_TYPE_NOTIFICATION:
1206 if (holder.entry.extraInfo != null) {
1207 holder.summary.setText(InstalledAppDetails.getNotificationSummary(
1208 (AppRow) holder.entry.extraInfo, mContext));
1210 holder.summary.setText(null);
1214 case LIST_TYPE_USAGE_ACCESS:
1215 if (holder.entry.extraInfo != null) {
1216 holder.summary.setText((new UsageState((PermissionState) holder.entry
1217 .extraInfo)).isPermissible() ? R.string.switch_on_text :
1218 R.string.switch_off_text);
1220 holder.summary.setText(null);
1224 case LIST_TYPE_HIGH_POWER:
1225 holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry));
1228 case LIST_TYPE_OVERLAY:
1229 holder.summary.setText(DrawOverlayDetails.getSummary(mContext, holder.entry));
1232 case LIST_TYPE_WRITE_SETTINGS:
1233 holder.summary.setText(WriteSettingsDetails.getSummary(mContext,
1237 case LIST_TYPE_MANAGE_SOURCES:
1239 .setText(((InstallAppsState) holder.entry.extraInfo).getSummary());
1243 holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
1249 public Filter getFilter() {
1254 public void onMovedToScrapHeap(View view) {
1255 mActive.remove(view);
1259 public Object[] getSections() {
1264 public int getPositionForSection(int sectionIndex) {
1265 return mSections[sectionIndex].position;
1269 public int getSectionForPosition(int position) {
1270 return mPositionToSectionIndex[position];
1274 private static class SummaryProvider implements SummaryLoader.SummaryProvider {
1276 private final Context mContext;
1277 private final SummaryLoader mLoader;
1278 private ApplicationsState.Session mSession;
1280 private SummaryProvider(Context context, SummaryLoader loader) {
1286 public void setListening(boolean listening) {
1288 new InstalledAppCounter(mContext, ApplicationFeatureProvider.IGNORE_INSTALL_REASON,
1289 new PackageManagerWrapperImpl(mContext.getPackageManager())) {
1291 protected void onCountComplete(int num) {
1292 mLoader.setSummary(SummaryProvider.this,
1293 mContext.getString(R.string.apps_summary, num));
1297 protected List<UserInfo> getUsersToCount() {
1298 return mUm.getProfiles(UserHandle.myUserId());
1305 private static class SectionInfo {
1309 public SectionInfo(String label, int position) {
1311 this.position = position;
1315 public String toString() {
1320 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
1321 = new SummaryLoader.SummaryProviderFactory() {
1323 public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
1324 SummaryLoader summaryLoader) {
1325 return new SummaryProvider(activity, summaryLoader);