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.IntentFilterVerificationInfo;
24 import android.content.pm.PackageManager;
25 import android.os.Bundle;
26 import android.os.Environment;
27 import android.os.UserHandle;
28 import android.os.UserManager;
29 import android.preference.PreferenceFrameLayout;
30 import android.provider.Settings;
31 import android.util.ArraySet;
32 import android.util.Log;
33 import android.view.LayoutInflater;
34 import android.view.Menu;
35 import android.view.MenuInflater;
36 import android.view.MenuItem;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.view.animation.AnimationUtils;
40 import android.widget.AbsListView;
41 import android.widget.AdapterView;
42 import android.widget.AdapterView.OnItemClickListener;
43 import android.widget.AdapterView.OnItemSelectedListener;
44 import android.widget.ArrayAdapter;
45 import android.widget.BaseAdapter;
46 import android.widget.Filter;
47 import android.widget.Filterable;
48 import android.widget.FrameLayout;
49 import android.widget.ListView;
50 import android.widget.Spinner;
52 import com.android.internal.logging.MetricsLogger;
53 import com.android.settings.AppHeader;
54 import com.android.settings.HelpUtils;
55 import com.android.settings.InstrumentedFragment;
56 import com.android.settings.R;
57 import com.android.settings.Settings.AllApplicationsActivity;
58 import com.android.settings.Settings.DomainsURLsAppListActivity;
59 import com.android.settings.Settings.HighPowerApplicationsActivity;
60 import com.android.settings.Settings.NotificationAppListActivity;
61 import com.android.settings.Settings.StorageUseActivity;
62 import com.android.settings.Settings.UsageAccessSettingsActivity;
63 import com.android.settings.SettingsActivity;
64 import com.android.settings.Utils;
65 import com.android.settings.applications.AppStateUsageBridge.UsageState;
66 import com.android.settings.applications.ApplicationsState.AppEntry;
67 import com.android.settings.applications.ApplicationsState.AppFilter;
68 import com.android.settings.applications.ApplicationsState.CompoundFilter;
69 import com.android.settings.applications.ApplicationsState.VolumeFilter;
70 import com.android.settings.fuelgauge.HighPowerDetail;
71 import com.android.settings.notification.AppNotificationSettings;
72 import com.android.settings.notification.NotificationBackend;
73 import com.android.settings.notification.NotificationBackend.AppRow;
75 import java.util.ArrayList;
76 import java.util.Collections;
77 import java.util.Comparator;
78 import java.util.List;
81 * Activity to pick an application that will be used to display installation information and
82 * options to uninstall/delete user data for system applications. This activity
83 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
86 public class ManageApplications extends InstrumentedFragment
87 implements OnItemClickListener, OnItemSelectedListener {
89 static final String TAG = "ManageApplications";
90 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
93 public static final String EXTRA_CLASSNAME = "classname";
94 // Used for storage only.
95 public static final String EXTRA_VOLUME_UUID = "volumeUuid";
96 public static final String EXTRA_VOLUME_NAME = "volumeName";
98 private static final String EXTRA_SORT_ORDER = "sortOrder";
100 // attributes used as keys when passing values to InstalledAppDetails activity
101 public static final String APP_CHG = "chg";
103 // constant value that can be used to check return code from sub activity.
104 private static final int INSTALLED_APP_DETAILS = 1;
105 private static final int ADVANCED_SETTINGS = 2;
107 public static final int SIZE_TOTAL = 0;
108 public static final int SIZE_INTERNAL = 1;
109 public static final int SIZE_EXTERNAL = 2;
111 // Filter options used for displayed list of applications
112 // The order which they appear is the order they will show when spinner is present.
113 public static final int FILTER_APPS_POWER_WHITELIST = 0;
114 public static final int FILTER_APPS_ALL = 1;
115 public static final int FILTER_APPS_ENABLED = 2;
116 public static final int FILTER_APPS_DISABLED = 3;
117 public static final int FILTER_APPS_BLOCKED = 4;
118 public static final int FILTER_APPS_PRIORITY = 5;
119 public static final int FILTER_APPS_NO_PEEKING = 6;
120 public static final int FILTER_APPS_SENSITIVE = 7;
121 public static final int FILTER_APPS_PERSONAL = 8;
122 public static final int FILTER_APPS_WORK = 9;
123 public static final int FILTER_APPS_WITH_DOMAIN_URLS = 10;
124 public static final int FILTER_APPS_USAGE_ACCESS = 11;
126 // This is the string labels for the filter modes above, the order must be kept in sync.
127 public static final int[] FILTER_LABELS = new int[] {
128 R.string.high_power_filter_on, // High power whitelist, on
129 R.string.filter_all_apps, // All apps
130 R.string.filter_enabled_apps, // Enabled
131 R.string.filter_apps_disabled, // Disabled
132 R.string.filter_notif_blocked_apps, // Blocked Notifications
133 R.string.filter_notif_priority_apps, // Priority Notifications
134 R.string.filter_notif_no_peeking, // No peeking Notifications
135 R.string.filter_notif_sensitive_apps, // Sensitive Notifications
136 R.string.filter_personal_apps, // Personal
137 R.string.filter_work_apps, // Work
138 R.string.filter_with_domain_urls_apps, // Domain URLs
139 R.string.filter_all_apps, // Usage access screen, never displayed
141 // This is the actual mapping to filters from FILTER_ constants above, the order must
143 public static final AppFilter[] FILTERS = new AppFilter[] {
144 AppStatePowerBridge.FILTER_POWER_WHITELISTED, // High power whitelist, on
145 ApplicationsState.FILTER_EVERYTHING, // All apps
146 ApplicationsState.FILTER_ALL_ENABLED, // Enabled
147 ApplicationsState.FILTER_DISABLED, // Disabled
148 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED, // Blocked Notifications
149 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_PRIORITY, // Priority Notifications
150 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_NO_PEEK, // No peeking Notifications
151 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_SENSITIVE, // Sensitive Notifications
152 ApplicationsState.FILTER_PERSONAL, // Personal
153 ApplicationsState.FILTER_WORK, // Work
154 ApplicationsState.FILTER_WITH_DOMAIN_URLS, // Apps with Domain URLs
155 AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs
159 private int mSortOrder = R.id.sort_order_alpha;
161 // whether showing system apps.
162 private boolean mShowSystem;
164 private ApplicationsState mApplicationsState;
166 public int mListType;
169 public ApplicationsAdapter mApplications;
171 private View mLoadingContainer;
173 private View mListContainer;
175 // ListView used to display list
176 private ListView mListView;
178 // Size resource used for packages whose size computation failed for some reason
179 CharSequence mInvalidSizeStr;
181 // layout inflater object used to inflate views
182 private LayoutInflater mInflater;
184 private String mCurrentPkgName;
185 private int mCurrentUid;
187 private Menu mOptionsMenu;
189 public static final int LIST_TYPE_MAIN = 0;
190 public static final int LIST_TYPE_NOTIFICATION = 1;
191 public static final int LIST_TYPE_DOMAINS_URLS = 2;
192 public static final int LIST_TYPE_STORAGE = 3;
193 public static final int LIST_TYPE_USAGE_ACCESS = 4;
194 public static final int LIST_TYPE_HIGH_POWER = 5;
196 private View mRootView;
198 private View mSpinnerHeader;
199 private Spinner mFilterSpinner;
200 private FilterSpinnerAdapter mFilterAdapter;
201 private NotificationBackend mNotifBackend;
202 private ResetAppsHelper mResetAppsHelper;
203 private String mVolumeUuid;
204 private String mVolumeName;
207 public void onCreate(Bundle savedInstanceState) {
208 super.onCreate(savedInstanceState);
209 setHasOptionsMenu(true);
210 mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
212 Intent intent = getActivity().getIntent();
213 Bundle args = getArguments();
214 String className = args != null ? args.getString(EXTRA_CLASSNAME) : null;
215 if (className == null) {
216 className = intent.getComponent().getClassName();
218 if (className.equals(AllApplicationsActivity.class.getName())) {
220 } else if (className.equals(NotificationAppListActivity.class.getName())) {
221 mListType = LIST_TYPE_NOTIFICATION;
222 mNotifBackend = new NotificationBackend();
223 } else if (className.equals(DomainsURLsAppListActivity.class.getName())) {
224 mListType = LIST_TYPE_DOMAINS_URLS;
225 } else if (className.equals(StorageUseActivity.class.getName())) {
226 if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) {
227 mVolumeUuid = args.getString(EXTRA_VOLUME_UUID);
228 mVolumeName = args.getString(EXTRA_VOLUME_NAME);
229 mListType = LIST_TYPE_STORAGE;
231 // No volume selected, display a normal list, sorted by size.
232 mListType = LIST_TYPE_MAIN;
234 mSortOrder = R.id.sort_order_size;
235 } else if (className.equals(UsageAccessSettingsActivity.class.getName())) {
236 mListType = LIST_TYPE_USAGE_ACCESS;
237 getActivity().getActionBar().setTitle(R.string.usage_access_title);
238 } else if (className.equals(HighPowerApplicationsActivity.class.getName())) {
239 mListType = LIST_TYPE_HIGH_POWER;
240 // Default to showing system.
243 mListType = LIST_TYPE_MAIN;
245 mFilter = getDefaultFilter();
247 if (savedInstanceState != null) {
248 mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
251 mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
253 mResetAppsHelper = new ResetAppsHelper(getActivity());
258 public View onCreateView(LayoutInflater inflater, ViewGroup container,
259 Bundle savedInstanceState) {
260 // initialize the inflater
261 mInflater = inflater;
263 mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
264 mLoadingContainer = mRootView.findViewById(R.id.loading_container);
265 mLoadingContainer.setVisibility(View.VISIBLE);
266 mListContainer = mRootView.findViewById(R.id.list_container);
267 if (mListContainer != null) {
268 // Create adapter and list view here
269 View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
270 ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
271 if (emptyView != null) {
272 lv.setEmptyView(emptyView);
274 lv.setOnItemClickListener(this);
275 lv.setSaveEnabled(true);
276 lv.setItemsCanFocus(true);
277 lv.setTextFilterEnabled(true);
279 mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
280 mListView.setAdapter(mApplications);
281 mListView.setRecyclerListener(mApplications);
283 Utils.prepareCustomPreferencesList(container, mRootView, mListView, false);
286 // We have to do this now because PreferenceFrameLayout looks at it
287 // only when the view is added.
288 if (container instanceof PreferenceFrameLayout) {
289 ((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true;
294 mResetAppsHelper.onRestoreInstanceState(savedInstanceState);
299 private void createHeader() {
300 Activity activity = getActivity();
301 FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
302 mSpinnerHeader = (ViewGroup) activity.getLayoutInflater()
303 .inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
304 mFilterSpinner = (Spinner) mSpinnerHeader.findViewById(R.id.filter_spinner);
305 mFilterAdapter = new FilterSpinnerAdapter(this);
306 mFilterSpinner.setAdapter(mFilterAdapter);
307 mFilterSpinner.setOnItemSelectedListener(this);
308 pinnedHeader.addView(mSpinnerHeader, 0);
310 mFilterAdapter.enableFilter(getDefaultFilter());
311 if (mListType == LIST_TYPE_MAIN || mListType == LIST_TYPE_NOTIFICATION) {
312 if (UserManager.get(getActivity()).getUserProfiles().size() > 1) {
313 mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL);
314 mFilterAdapter.enableFilter(FILTER_APPS_WORK);
317 if (mListType == LIST_TYPE_NOTIFICATION) {
318 mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED);
319 mFilterAdapter.enableFilter(FILTER_APPS_PRIORITY);
320 mFilterAdapter.enableFilter(FILTER_APPS_SENSITIVE);
321 mFilterAdapter.enableFilter(FILTER_APPS_NO_PEEKING);
323 if (mListType == LIST_TYPE_HIGH_POWER) {
324 mFilterAdapter.enableFilter(FILTER_APPS_ALL);
326 if (mListType == LIST_TYPE_STORAGE) {
327 mApplications.setOverrideFilter(new VolumeFilter(mVolumeUuid));
332 public void onViewCreated(View view, Bundle savedInstanceState) {
333 super.onViewCreated(view, savedInstanceState);
334 if (mListType == LIST_TYPE_STORAGE) {
335 FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
336 AppHeader.createAppHeader(getActivity(), null, mVolumeName, null, pinnedHeader);
340 private int getDefaultFilter() {
342 case LIST_TYPE_DOMAINS_URLS:
343 return FILTER_APPS_WITH_DOMAIN_URLS;
344 case LIST_TYPE_USAGE_ACCESS:
345 return FILTER_APPS_USAGE_ACCESS;
346 case LIST_TYPE_HIGH_POWER:
347 return FILTER_APPS_POWER_WHITELIST;
349 return FILTER_APPS_ALL;
354 protected int getMetricsCategory() {
357 return MetricsLogger.MANAGE_APPLICATIONS;
358 case LIST_TYPE_NOTIFICATION:
359 return MetricsLogger.MANAGE_APPLICATIONS_NOTIFICATIONS;
360 case LIST_TYPE_DOMAINS_URLS:
361 return MetricsLogger.MANAGE_DOMAIN_URLS;
362 case LIST_TYPE_STORAGE:
363 return MetricsLogger.APPLICATIONS_STORAGE_APPS;
364 case LIST_TYPE_USAGE_ACCESS:
365 return MetricsLogger.USAGE_ACCESS;
366 case LIST_TYPE_HIGH_POWER:
367 return MetricsLogger.APPLICATIONS_HIGH_POWER_APPS;
369 return MetricsLogger.VIEW_UNKNOWN;
374 public void onResume() {
378 if (mApplications != null) {
379 mApplications.resume(mSortOrder);
380 mApplications.updateLoading();
385 public void onSaveInstanceState(Bundle outState) {
386 super.onSaveInstanceState(outState);
387 mResetAppsHelper.onSaveInstanceState(outState);
388 outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
392 public void onPause() {
394 if (mApplications != null) {
395 mApplications.pause();
400 public void onStop() {
402 mResetAppsHelper.stop();
406 public void onDestroyView() {
407 super.onDestroyView();
409 if (mApplications != null) {
410 mApplications.release();
416 public void onActivityResult(int requestCode, int resultCode, Intent data) {
417 if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
418 if (mListType == LIST_TYPE_NOTIFICATION) {
419 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
421 mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid));
426 // utility method used to start sub activity
427 private void startApplicationDetailsActivity() {
429 case LIST_TYPE_NOTIFICATION:
430 startAppInfoFragment(AppNotificationSettings.class,
431 R.string.app_notifications_title);
433 case LIST_TYPE_DOMAINS_URLS:
434 startAppInfoFragment(AppLaunchSettings.class, R.string.auto_launch_label);
436 case LIST_TYPE_USAGE_ACCESS:
437 startAppInfoFragment(UsageAccessDetails.class, R.string.usage_access);
439 case LIST_TYPE_STORAGE:
440 startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings);
442 case LIST_TYPE_HIGH_POWER:
443 startAppInfoFragment(HighPowerDetail.class, R.string.high_power_apps);
445 // TODO: Figure out if there is a way where we can spin up the profile's settings
446 // process ahead of time, to avoid a long load of data when user clicks on a managed app.
447 // Maybe when they load the list of apps that contains managed profile apps.
449 startAppInfoFragment(InstalledAppDetails.class, R.string.application_info_label);
454 private void startAppInfoFragment(Class<?> fragment, int titleRes) {
455 AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this,
456 INSTALLED_APP_DETAILS);
460 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
461 if (mListType == LIST_TYPE_DOMAINS_URLS) {
464 HelpUtils.prepareHelpMenuItem(getActivity(), menu, mListType == LIST_TYPE_MAIN
465 ? R.string.help_uri_apps : R.string.help_uri_notifications);
467 inflater.inflate(R.menu.manage_apps, menu);
472 public void onPrepareOptionsMenu(Menu menu) {
477 public void onDestroyOptionsMenu() {
481 void updateOptionsMenu() {
482 if (mOptionsMenu == null) {
485 mOptionsMenu.findItem(R.id.advanced).setVisible(mListType == LIST_TYPE_MAIN);
487 mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE
488 && mSortOrder != R.id.sort_order_alpha);
489 mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE
490 && mSortOrder != R.id.sort_order_size);
492 mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem);
493 mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem);
497 public boolean onOptionsItemSelected(MenuItem item) {
498 int menuId = item.getItemId();
499 switch(item.getItemId()) {
500 case R.id.sort_order_alpha:
501 case R.id.sort_order_size:
503 if (mApplications != null) {
504 mApplications.rebuild(mSortOrder);
507 case R.id.show_system:
508 case R.id.hide_system:
509 mShowSystem = !mShowSystem;
510 mApplications.rebuild(false);
512 case R.id.reset_app_preferences:
513 mResetAppsHelper.buildResetDialog();
516 ((SettingsActivity) getActivity()).startPreferencePanel(
517 AdvancedAppSettings.class.getName(), null, R.string.advanced_apps,
518 null, this, ADVANCED_SETTINGS);
521 // Handle the home button
529 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
530 if (mApplications != null && mApplications.getCount() > position) {
531 ApplicationsState.AppEntry entry = mApplications.getAppEntry(position);
532 mCurrentPkgName = entry.info.packageName;
533 mCurrentUid = entry.info.uid;
534 startApplicationDetailsActivity();
539 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
540 mFilter = mFilterAdapter.getFilter(position);
541 mApplications.setFilter(mFilter);
542 if (DEBUG) Log.d(TAG, "Selecting filter " + mFilter);
546 public void onNothingSelected(AdapterView<?> parent) {
549 public void updateView() {
551 final Activity host = getActivity();
553 host.invalidateOptionsMenu();
557 public void setHasDisabled(boolean hasDisabledApps) {
558 mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps);
559 mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps);
562 static class FilterSpinnerAdapter extends ArrayAdapter<CharSequence> {
564 private final ManageApplications mManageApplications;
566 // Use ArrayAdapter for view logic, but have our own list for managing
567 // the options available.
568 private final ArrayList<Integer> mFilterOptions = new ArrayList<>();
570 public FilterSpinnerAdapter(ManageApplications manageApplications) {
571 super(manageApplications.getActivity(), R.layout.filter_spinner_item);
572 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
573 mManageApplications = manageApplications;
576 public int getFilter(int position) {
577 return mFilterOptions.get(position);
580 public void setFilterEnabled(int filter, boolean enabled) {
582 enableFilter(filter);
584 disableFilter(filter);
588 public void enableFilter(int filter) {
589 if (mFilterOptions.contains(filter)) return;
590 if (DEBUG) Log.d(TAG, "Enabling filter " + filter);
591 mFilterOptions.add(filter);
592 Collections.sort(mFilterOptions);
593 mManageApplications.mSpinnerHeader.setVisibility(
594 mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
595 notifyDataSetChanged();
596 if (mFilterOptions.size() == 1) {
597 if (DEBUG) Log.d(TAG, "Auto selecting filter " + filter);
598 mManageApplications.mFilterSpinner.setSelection(0);
599 mManageApplications.onItemSelected(null, null, 0, 0);
603 public void disableFilter(int filter) {
604 if (!mFilterOptions.remove((Integer) filter)) {
607 if (DEBUG) Log.d(TAG, "Disabling filter " + filter);
608 Collections.sort(mFilterOptions);
609 mManageApplications.mSpinnerHeader.setVisibility(
610 mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
611 notifyDataSetChanged();
612 if (mManageApplications.mFilter == filter) {
613 if (mFilterOptions.size() > 0) {
614 if (DEBUG) Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0));
615 mManageApplications.mFilterSpinner.setSelection(0);
616 mManageApplications.onItemSelected(null, null, 0, 0);
622 public int getCount() {
623 return mFilterOptions.size();
627 public CharSequence getItem(int position) {
628 return getFilterString(mFilterOptions.get(position));
631 private CharSequence getFilterString(int filter) {
632 return mManageApplications.getString(FILTER_LABELS[filter]);
638 * Custom adapter implementation for the ListView
639 * This adapter maintains a map for each displayed application and its properties
640 * An index value on each AppInfo object indicates the correct position or index
641 * in the list. If the list gets updated dynamically when the user is viewing the list of
642 * applications, we need to return the correct index of position. This is done by mapping
643 * the getId methods via the package name into the internal maps and indices.
644 * The order of applications in the list is mirrored in mAppLocalList
646 static class ApplicationsAdapter extends BaseAdapter implements Filterable,
647 ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
648 AbsListView.RecyclerListener {
649 private final ApplicationsState mState;
650 private final ApplicationsState.Session mSession;
651 private final ManageApplications mManageApplications;
652 private final Context mContext;
653 private final ArrayList<View> mActive = new ArrayList<View>();
654 private final AppStateBaseBridge mExtraInfoBridge;
655 private int mFilterMode;
656 private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
657 private ArrayList<ApplicationsState.AppEntry> mEntries;
658 private boolean mResumed;
659 private int mLastSortMode=-1;
660 private int mWhichSize = SIZE_TOTAL;
661 CharSequence mCurFilterPrefix;
662 private PackageManager mPm;
663 private AppFilter mOverrideFilter;
665 private Filter mFilter = new Filter() {
667 protected FilterResults performFiltering(CharSequence constraint) {
668 ArrayList<ApplicationsState.AppEntry> entries
669 = applyPrefixFilter(constraint, mBaseEntries);
670 FilterResults fr = new FilterResults();
672 fr.count = entries.size();
677 @SuppressWarnings("unchecked")
678 protected void publishResults(CharSequence constraint, FilterResults results) {
679 mCurFilterPrefix = constraint;
680 mEntries = (ArrayList<ApplicationsState.AppEntry>) results.values;
681 notifyDataSetChanged();
685 public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
688 mSession = state.newSession(this);
689 mManageApplications = manageApplications;
690 mContext = manageApplications.getActivity();
691 mPm = mContext.getPackageManager();
692 mFilterMode = filterMode;
693 if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
694 mExtraInfoBridge = new AppStateNotificationBridge(mContext.getPackageManager(),
695 mState, this, manageApplications.mNotifBackend);
696 } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
697 mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
698 } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) {
699 mExtraInfoBridge = new AppStatePowerBridge(mState, this);
701 mExtraInfoBridge = null;
705 public void setOverrideFilter(AppFilter overrideFilter) {
706 mOverrideFilter = overrideFilter;
710 public void setFilter(int filter) {
711 mFilterMode = filter;
715 public void resume(int sort) {
716 if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed);
720 mLastSortMode = sort;
721 if (mExtraInfoBridge != null) {
722 mExtraInfoBridge.resume();
730 public void pause() {
734 if (mExtraInfoBridge != null) {
735 mExtraInfoBridge.pause();
740 public void release() {
742 if (mExtraInfoBridge != null) {
743 mExtraInfoBridge.release();
747 public void rebuild(int sort) {
748 if (sort == mLastSortMode) {
751 mLastSortMode = sort;
755 public void rebuild(boolean eraseold) {
756 if (DEBUG) Log.i(TAG, "Rebuilding app list...");
757 ApplicationsState.AppFilter filterObj;
758 Comparator<AppEntry> comparatorObj;
759 boolean emulated = Environment.isExternalStorageEmulated();
761 mWhichSize = SIZE_TOTAL;
763 mWhichSize = SIZE_INTERNAL;
765 filterObj = FILTERS[mFilterMode];
766 if (mOverrideFilter != null) {
767 filterObj = mOverrideFilter;
769 if (!mManageApplications.mShowSystem) {
770 filterObj = new CompoundFilter(filterObj,
771 ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
773 switch (mLastSortMode) {
774 case R.id.sort_order_size:
775 switch (mWhichSize) {
777 comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
780 comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
783 comparatorObj = ApplicationsState.SIZE_COMPARATOR;
788 comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
791 ArrayList<ApplicationsState.AppEntry> entries
792 = mSession.rebuild(filterObj, comparatorObj);
793 if (entries == null && !eraseold) {
794 // Don't have new list yet, but can continue using the old one.
797 mBaseEntries = entries;
798 if (mBaseEntries != null) {
799 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
803 notifyDataSetChanged();
805 if (mSession.getAllApps().size() != 0
806 && mManageApplications.mListContainer.getVisibility() != View.VISIBLE) {
807 Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
808 mManageApplications.mListContainer, true, true);
810 if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
811 // No enabled or disabled filters for usage access.
815 mManageApplications.setHasDisabled(mState.haveDisabledApps());
818 private void updateLoading() {
819 Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
820 mManageApplications.mListContainer, mSession.getAllApps().size() != 0, false);
823 private boolean hasDisabledApps() {
824 ArrayList<AppEntry> allApps = mSession.getAllApps();
825 for (int i = 0; i < allApps.size(); i++) {
826 if (!allApps.get(i).info.enabled) {
833 ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
834 ArrayList<ApplicationsState.AppEntry> origEntries) {
835 if (prefix == null || prefix.length() == 0) {
838 String prefixStr = ApplicationsState.normalize(prefix.toString());
839 final String spacePrefixStr = " " + prefixStr;
840 ArrayList<ApplicationsState.AppEntry> newEntries
841 = new ArrayList<ApplicationsState.AppEntry>();
842 for (int i=0; i<origEntries.size(); i++) {
843 ApplicationsState.AppEntry entry = origEntries.get(i);
844 String nlabel = entry.getNormalizedLabel();
845 if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
846 newEntries.add(entry);
854 public void onExtraInfoUpdated() {
859 public void onRunningStateChanged(boolean running) {
860 mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running);
864 public void onRebuildComplete(ArrayList<AppEntry> apps) {
865 if (mManageApplications.mLoadingContainer.getVisibility() == View.VISIBLE) {
866 mManageApplications.mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
867 mContext, android.R.anim.fade_out));
868 mManageApplications.mListContainer.startAnimation(AnimationUtils.loadAnimation(
869 mContext, android.R.anim.fade_in));
871 mManageApplications.mListContainer.setVisibility(View.VISIBLE);
872 mManageApplications.mLoadingContainer.setVisibility(View.GONE);
874 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
875 notifyDataSetChanged();
879 public void onPackageListChanged() {
884 public void onPackageIconChanged() {
885 // We ensure icons are loaded when their item is displayed, so
886 // don't care about icons loaded in the background.
890 public void onLoadEntriesCompleted() {
895 public void onPackageSizeChanged(String packageName) {
896 for (int i=0; i<mActive.size(); i++) {
897 AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag();
898 if (holder.entry.info.packageName.equals(packageName)) {
899 synchronized (holder.entry) {
900 updateSummary(holder);
902 if (holder.entry.info.packageName.equals(mManageApplications.mCurrentPkgName)
903 && mLastSortMode == R.id.sort_order_size) {
904 // We got the size information for the last app the
905 // user viewed, and are sorting by size... they may
906 // have cleared data, so we immediately want to resort
907 // the list with the new size to reflect it to the user.
916 public void onLauncherInfoChanged() {
917 if (!mManageApplications.mShowSystem) {
923 public void onAllSizesComputed() {
924 if (mLastSortMode == R.id.sort_order_size) {
929 public int getCount() {
930 return mEntries != null ? mEntries.size() : 0;
933 public Object getItem(int position) {
934 return mEntries.get(position);
937 public ApplicationsState.AppEntry getAppEntry(int position) {
938 return mEntries.get(position);
941 public long getItemId(int position) {
942 return mEntries.get(position).id;
946 public boolean areAllItemsEnabled() {
950 public View getView(int position, View convertView, ViewGroup parent) {
951 // A ViewHolder keeps references to children views to avoid unnecessary calls
952 // to findViewById() on each row.
953 AppViewHolder holder = AppViewHolder.createOrRecycle(mManageApplications.mInflater,
955 convertView = holder.rootView;
957 // Bind the data efficiently with the holder
958 ApplicationsState.AppEntry entry = mEntries.get(position);
959 synchronized (entry) {
960 holder.entry = entry;
961 if (entry.label != null) {
962 holder.appName.setText(entry.label);
964 mState.ensureIcon(entry);
965 if (entry.icon != null) {
966 holder.appIcon.setImageDrawable(entry.icon);
968 updateSummary(holder);
969 if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
970 holder.disabled.setVisibility(View.VISIBLE);
971 holder.disabled.setText(R.string.not_installed);
972 } else if (!entry.info.enabled) {
973 holder.disabled.setVisibility(View.VISIBLE);
974 holder.disabled.setText(R.string.disabled);
976 holder.disabled.setVisibility(View.GONE);
978 holder.checkBox.setVisibility(View.GONE);
980 mActive.remove(convertView);
981 mActive.add(convertView);
985 private void updateSummary(AppViewHolder holder) {
986 switch (mManageApplications.mListType) {
987 case LIST_TYPE_NOTIFICATION:
988 if (holder.entry.extraInfo != null) {
989 holder.summary.setText(InstalledAppDetails.getNotificationSummary(
990 (AppRow) holder.entry.extraInfo, mContext));
992 holder.summary.setText(null);
996 case LIST_TYPE_DOMAINS_URLS:
997 holder.summary.setText(getDomainsSummary(holder.entry.info.packageName));
1000 case LIST_TYPE_USAGE_ACCESS:
1001 if (holder.entry.extraInfo != null) {
1002 holder.summary.setText(((UsageState) holder.entry.extraInfo).hasAccess() ?
1003 R.string.switch_on_text : R.string.switch_off_text);
1005 holder.summary.setText(null);
1009 case LIST_TYPE_HIGH_POWER:
1010 holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry));
1014 holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize);
1020 public Filter getFilter() {
1025 public void onMovedToScrapHeap(View view) {
1026 mActive.remove(view);
1029 private CharSequence getDomainsSummary(String packageName) {
1030 ArraySet<String> result = new ArraySet<>();
1031 List<IntentFilterVerificationInfo> list =
1032 mPm.getIntentFilterVerifications(packageName);
1033 for (IntentFilterVerificationInfo ivi : list) {
1034 for (String host : ivi.getDomains()) {
1038 if (result.size() == 0) {
1039 return mContext.getString(R.string.domain_urls_summary_none);
1040 } else if (result.size() == 1) {
1041 return mContext.getString(R.string.domain_urls_summary_one, result.valueAt(0));
1043 return mContext.getString(R.string.domain_urls_summary_some, result.valueAt(0));