From 5b3aa36e6fd17db995a31f9549b8fe628754a750 Mon Sep 17 00:00:00 2001 From: Jason Monk Date: Wed, 18 Mar 2015 16:03:57 -0400 Subject: [PATCH] Implement new Manage Apps list Manage apps now only has one list. When entered from the dashboard it is not sortable or filterable, and shows a combination of downloaded apps and anything that shows up in launcher. When 'all apps' is entered through advanced app settings, the list shows all apps and can be filtered and sorted. Bug: 19443900 Change-Id: I555ee56417c330424d458750b6f81d3bd543f66d --- AndroidManifest.xml | 14 +- res/drawable/ic_settings_24dp.xml | 25 + res/layout/apps_filter_spinner.xml | 41 + res/layout/filter_spinner_item.xml | 27 + res/layout/manage_applications_content.xml | 43 - res/menu/manage_apps.xml | 23 + res/values/strings.xml | 29 +- res/xml/advanced_apps.xml | 38 + src/com/android/settings/Settings.java | 1 + .../applications/AdvancedAppSettings.java | 257 ++++ .../settings/applications/AppInfoBase.java | 5 + .../applications/ApplicationsState.java | 184 ++- .../applications/ManageApplications.java | 1306 ++++++----------- src/com/android/settings/search/Ranking.java | 25 +- .../search/SearchIndexableResources.java | 8 + 15 files changed, 1024 insertions(+), 1002 deletions(-) create mode 100644 res/drawable/ic_settings_24dp.xml create mode 100644 res/layout/apps_filter_spinner.xml create mode 100644 res/layout/filter_spinner_item.xml delete mode 100644 res/layout/manage_applications_content.xml create mode 100644 res/menu/manage_apps.xml create mode 100644 res/xml/advanced_apps.xml create mode 100644 src/com/android/settings/applications/AdvancedAppSettings.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 8eccf9ec333..883ded3d915 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -759,7 +759,6 @@ - @@ -774,6 +773,19 @@ android:resource="@id/application_settings" /> + + + + + + + + + + + + + diff --git a/res/layout/apps_filter_spinner.xml b/res/layout/apps_filter_spinner.xml new file mode 100644 index 00000000000..f72d7eefb8a --- /dev/null +++ b/res/layout/apps_filter_spinner.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + diff --git a/res/layout/filter_spinner_item.xml b/res/layout/filter_spinner_item.xml new file mode 100644 index 00000000000..e83dd8071ed --- /dev/null +++ b/res/layout/filter_spinner_item.xml @@ -0,0 +1,27 @@ + + + + diff --git a/res/layout/manage_applications_content.xml b/res/layout/manage_applications_content.xml deleted file mode 100644 index 364a00265e8..00000000000 --- a/res/layout/manage_applications_content.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - diff --git a/res/menu/manage_apps.xml b/res/menu/manage_apps.xml new file mode 100644 index 00000000000..577e9812e5a --- /dev/null +++ b/res/menu/manage_apps.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index c202a898090..be27f83a05d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5626,6 +5626,8 @@ account restriction restrict restricted text correction correct sound vibrate auto language gesture suggest suggestion theme offensive word type emoji international + reset preferences default + apps download applications system slide password pattern pin @@ -6088,10 +6090,29 @@ %d permissions granted - - Some defaults set - - No defaults set + + Some defaults set + + No defaults set + + + All apps + + Enabled + + Personal + + Work + + + Reset preferences across all apps to defaults + + + %d apps installed, including system and downloaded apps + + + Advanced Unknown app diff --git a/res/xml/advanced_apps.xml b/res/xml/advanced_apps.xml new file mode 100644 index 00000000000..a6cec1375e2 --- /dev/null +++ b/res/xml/advanced_apps.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index b16fe811d09..20ce116e62b 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -47,6 +47,7 @@ public class Settings extends SettingsActivity { public static class DeviceInfoSettingsActivity extends SettingsActivity { /* empty */ } public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ } public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ } + public static class AllApplicationsActivity extends SettingsActivity { /* empty */ } public static class AppOpsSummaryActivity extends SettingsActivity { @Override public boolean isValidFragment(String className) { diff --git a/src/com/android/settings/applications/AdvancedAppSettings.java b/src/com/android/settings/applications/AdvancedAppSettings.java new file mode 100644 index 00000000000..6692d6b8507 --- /dev/null +++ b/src/com/android/settings/applications/AdvancedAppSettings.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.applications; + +import static android.net.NetworkPolicyManager.POLICY_NONE; +import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; + +import android.app.ActivityManager; +import android.app.AlertDialog; +import android.app.AppOpsManager; +import android.app.INotificationManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.net.NetworkPolicyManager; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.applications.ApplicationsState.AppEntry; +import com.android.settings.applications.ApplicationsState.Callbacks; +import com.android.settings.applications.ApplicationsState.Session; + +import java.util.ArrayList; +import java.util.List; + +public class AdvancedAppSettings extends SettingsPreferenceFragment implements Callbacks, + DialogInterface.OnClickListener, DialogInterface.OnDismissListener { + + static final String TAG = "AdvancedAppSettings"; + static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private static final String KEY_ALL_APPS = "all_apps"; + private static final String KEY_RESET_ALL = "reset_all"; + private static final String EXTRA_RESET_DIALOG = "resetDialog"; + + private ApplicationsState mApplicationsState; + private Session mSession; + private Preference mAllApps; + private Preference mResetAll; + + AlertDialog mResetDialog; + + private boolean mActivityResumed; + private PackageManager mPm; + private IPackageManager mIPm; + private INotificationManager mNm; + private NetworkPolicyManager mNpm; + private AppOpsManager mAom; + private Handler mHandler; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.advanced_apps); + + mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); + mSession = mApplicationsState.newSession(this); + + mAllApps = findPreference(KEY_ALL_APPS); + mResetAll = findPreference(KEY_RESET_ALL); + mResetAll.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + buildResetDialog(); + return true; + } + }); + updateAllAppsSummary(); + + mPm = getActivity().getPackageManager(); + mIPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); + mNm = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + mNpm = NetworkPolicyManager.from(getActivity()); + mAom = (AppOpsManager)getActivity().getSystemService(Context.APP_OPS_SERVICE); + mHandler = new Handler(getActivity().getMainLooper()); + } + + private void updateAllAppsSummary() { + mAllApps.setSummary(getString(R.string.all_apps_summary, mSession.getAllApps().size())); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_RESET_DIALOG)) { + buildResetDialog(); + } + + return super.onCreateView(inflater, container, savedInstanceState); + } + + @Override + public void onStop() { + super.onStop(); + if (mResetDialog != null) { + mResetDialog.dismiss(); + mResetDialog = null; + } + } + + @Override + public void onResume() { + super.onResume(); + mActivityResumed = true; + } + + @Override + public void onPause() { + mActivityResumed = false; + super.onPause(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if (mResetDialog != null) { + outState.putBoolean(EXTRA_RESET_DIALOG, true); + } + } + + void buildResetDialog() { + if (mResetDialog == null) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.reset_app_preferences_title); + builder.setMessage(R.string.reset_app_preferences_desc); + builder.setPositiveButton(R.string.reset_app_preferences_button, this); + builder.setNegativeButton(R.string.cancel, null); + mResetDialog = builder.show(); + mResetDialog.setOnDismissListener(this); + } + } + + @Override + public void onDismiss(DialogInterface dialog) { + if (mResetDialog == dialog) { + mResetDialog = null; + } + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (mResetDialog == dialog) { + (new AsyncTask() { + @Override protected Void doInBackground(Void... params) { + List apps = mPm.getInstalledApplications( + PackageManager.GET_DISABLED_COMPONENTS); + for (int i=0; i apps) { + // No-op. + } + + @Override + public void onPackageIconChanged() { + // No-op. + } + + @Override + public void onPackageSizeChanged(String packageName) { + // No-op. + } + + @Override + public void onAllSizesComputed() { + // No-op. + } + + @Override + public void onLauncherInfoChanged() { + // No-op. + } + +} diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java index 28f2925bef5..b438051e002 100644 --- a/src/com/android/settings/applications/AppInfoBase.java +++ b/src/com/android/settings/applications/AppInfoBase.java @@ -179,6 +179,11 @@ public abstract class AppInfoBase extends PreferenceFragment // No op. } + @Override + public void onLauncherInfoChanged() { + // No op. + } + @Override public void onPackageListChanged() { refreshUi(); diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java index 3825296e841..51a15cf8145 100644 --- a/src/com/android/settings/applications/ApplicationsState.java +++ b/src/com/android/settings/applications/ApplicationsState.java @@ -1,5 +1,6 @@ package com.android.settings.applications; +import android.app.ActivityManager; import android.app.AppGlobals; import android.app.Application; import android.content.BroadcastReceiver; @@ -12,6 +13,7 @@ import android.content.pm.IPackageStatsObserver; import android.content.pm.PackageManager; import android.content.pm.PackageStats; import android.content.pm.ParceledListSlice; +import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; @@ -54,11 +56,12 @@ public class ApplicationsState { public void onPackageIconChanged(); public void onPackageSizeChanged(String packageName); public void onAllSizesComputed(); + public void onLauncherInfoChanged(); } public static interface AppFilter { public void init(); - public boolean filterApp(ApplicationInfo info); + public boolean filterApp(AppEntry info); } static final int SIZE_UNKNOWN = -1; @@ -90,7 +93,7 @@ public class ApplicationsState { // for purposes of cleaning them up in the app details UI. long externalCacheSize; } - + public static class AppEntry extends SizeInfo { final File apkFile; final long id; @@ -100,7 +103,9 @@ public class ApplicationsState { long externalSize; boolean mounted; - + + boolean hasLauncherEntry; + String getNormalizedLabel() { if (normalizedLabel != null) { return normalizedLabel; @@ -128,7 +133,7 @@ public class ApplicationsState { this.sizeStale = true; ensureLabel(context); } - + void ensureLabel(Context context) { if (this.label == null || !this.mounted) { if (!this.apkFile.exists()) { @@ -141,7 +146,7 @@ public class ApplicationsState { } } } - + boolean ensureIconLocked(Context context, PackageManager pm) { if (this.icon == null) { if (this.apkFile.exists()) { @@ -175,13 +180,6 @@ public class ApplicationsState { private final Collator sCollator = Collator.getInstance(); @Override public int compare(AppEntry object1, AppEntry object2) { - final boolean normal1 = object1.info.enabled - && (object1.info.flags&ApplicationInfo.FLAG_INSTALLED) != 0; - final boolean normal2 = object2.info.enabled - && (object2.info.flags&ApplicationInfo.FLAG_INSTALLED) != 0; - if (normal1 != normal2) { - return normal1 ? -1 : 1; - } return sCollator.compare(object1.label, object2.label); } }; @@ -219,58 +217,105 @@ public class ApplicationsState { } }; - public static final AppFilter THIRD_PARTY_FILTER = new AppFilter() { + public static final AppFilter FILTER_PERSONAL = new AppFilter() { + private int mCurrentUser; + + public void init() { + mCurrentUser = ActivityManager.getCurrentUser(); + } + + @Override + public boolean filterApp(AppEntry entry) { + return UserHandle.getUserId(entry.info.uid) == mCurrentUser; + } + }; + + public static final AppFilter FILTER_WORK = new AppFilter() { + private int mCurrentUser; + + public void init() { + mCurrentUser = ActivityManager.getCurrentUser(); + } + + @Override + public boolean filterApp(AppEntry entry) { + return UserHandle.getUserId(entry.info.uid) != mCurrentUser; + } + }; + + public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() { public void init() { } - + @Override - public boolean filterApp(ApplicationInfo info) { - if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + public boolean filterApp(AppEntry entry) { + if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { return true; - } else if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + return true; + } else if (entry.hasLauncherEntry) { return true; } return false; } }; - public static final AppFilter ON_SD_CARD_FILTER = new AppFilter() { + public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() { + public void init() { + } + + @Override + public boolean filterApp(AppEntry entry) { + if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + return true; + } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + return true; + } + return false; + } + }; + + public static final AppFilter FILTER_ON_SD_CARD = new AppFilter() { final CanBeOnSdCardChecker mCanBeOnSdCardChecker = new CanBeOnSdCardChecker(); - + public void init() { mCanBeOnSdCardChecker.init(); } - + @Override - public boolean filterApp(ApplicationInfo info) { - return mCanBeOnSdCardChecker.check(info); + public boolean filterApp(AppEntry entry) { + return mCanBeOnSdCardChecker.check(entry.info); } }; - public static final AppFilter DISABLED_FILTER = new AppFilter() { + public static final AppFilter FILTER_DISABLED = new AppFilter() { public void init() { } - + @Override - public boolean filterApp(ApplicationInfo info) { - if (!info.enabled) { - return true; - } - return false; + public boolean filterApp(AppEntry entry) { + return !entry.info.enabled; } }; - public static final AppFilter ALL_ENABLED_FILTER = new AppFilter() { + public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() { public void init() { } - + @Override - public boolean filterApp(ApplicationInfo info) { - if (info.enabled) { - return true; - } - return false; + public boolean filterApp(AppEntry entry) { + return entry.info.enabled; + } + }; + + public static final AppFilter FILTER_EVERYTHING = new AppFilter() { + public void init() { + } + + @Override + public boolean filterApp(AppEntry entry) { + return true; } }; @@ -398,6 +443,7 @@ public class ApplicationsState { static final int MSG_PACKAGE_SIZE_CHANGED = 4; static final int MSG_ALL_SIZES_COMPUTED = 5; static final int MSG_RUNNING_STATE_CHANGED = 6; + static final int MSG_LAUNCHER_INFO_CHANGED = 7; @Override public void handleMessage(Message msg) { @@ -436,6 +482,11 @@ public class ApplicationsState { msg.arg1 != 0); } } break; + case MSG_LAUNCHER_INFO_CHANGED: { + for (int i=0; i 5 seconds * (leading to an ANR). - * + * * Dalvik will promote a monitor to a "real" lock if it detects enough * contention on it. It doesn't figure this out fast enough for us * here, though, so this little trick will force it to turn into a real @@ -543,6 +594,12 @@ public class ApplicationsState { } } + ArrayList getAllApps() { + synchronized (mEntriesMap) { + return new ArrayList<>(mAppEntries); + } + } + // Creates a new list of app entries with the given filter and comparator. ArrayList rebuild(AppFilter filter, Comparator comparator) { synchronized (mRebuildSync) { @@ -600,22 +657,21 @@ public class ApplicationsState { if (filter != null) { filter.init(); } - - List apps; + + List apps; synchronized (mEntriesMap) { - apps = new ArrayList(mApplications); + apps = new ArrayList<>(mAppEntries); } ArrayList filteredApps = new ArrayList(); if (DEBUG) Log.i(TAG, "Rebuilding..."); for (int i=0; i=0; i--) { ApplicationInfo appInfo = mApplications.get(i); @@ -962,6 +1018,7 @@ public class ApplicationsState { static final int MSG_LOAD_ENTRIES = 2; static final int MSG_LOAD_ICONS = 3; static final int MSG_LOAD_SIZES = 4; + static final int MSG_LOAD_LAUNCHER = 5; boolean mRunning; @@ -1074,9 +1131,40 @@ public class ApplicationsState { if (numDone >= 6) { sendEmptyMessage(MSG_LOAD_ENTRIES); } else { - sendEmptyMessage(MSG_LOAD_ICONS); + sendEmptyMessage(MSG_LOAD_LAUNCHER); } } break; + case MSG_LOAD_LAUNCHER: { + Intent launchIntent = new Intent(Intent.ACTION_MAIN, null) + .addCategory(Intent.CATEGORY_LAUNCHER); + + for (int i = 0; i < mEntriesMap.size(); i++) { + int userId = mEntriesMap.keyAt(i); + List intents = mPm.queryIntentActivitiesAsUser(launchIntent, + PackageManager.GET_DISABLED_COMPONENTS, userId); + synchronized (mEntriesMap) { + if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock"); + HashMap userEntries = mEntriesMap.valueAt(i); + final int N = intents.size(); + for (int j = 0; j < N; j++) { + String packageName = intents.get(j).activityInfo.packageName; + AppEntry entry = userEntries.get(packageName); + if (entry != null) { + entry.hasLauncherEntry = true; + } else { + Log.w(TAG, "Cannot find pkg: " + packageName + + " on user " + userId); + } + } + if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock"); + } + } + + if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) { + mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED); + } + sendEmptyMessage(MSG_LOAD_ICONS); + } break; case MSG_LOAD_ICONS: { int numDone = 0; synchronized (mEntriesMap) { diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java index 555d18ce784..14d48c8faf9 100644 --- a/src/com/android/settings/applications/ManageApplications.java +++ b/src/com/android/settings/applications/ManageApplications.java @@ -16,40 +16,22 @@ package com.android.settings.applications; -import static android.net.NetworkPolicyManager.POLICY_NONE; -import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; - import android.app.Activity; -import android.app.ActivityManager; -import android.app.AlertDialog; -import android.app.AppOpsManager; import android.app.Fragment; -import android.app.INotificationManager; -import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; -import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.res.TypedArray; -import android.net.NetworkPolicyManager; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; -import android.os.Handler; -import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.os.UserManager; import android.preference.PreferenceFrameLayout; import android.provider.Settings; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.PagerTabStrip; -import android.support.v4.view.ViewPager; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -61,34 +43,35 @@ import android.view.animation.AnimationUtils; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; import android.widget.ListView; import android.widget.Spinner; -import com.android.internal.app.IMediaContainerService; import com.android.internal.content.PackageHelper; import com.android.settings.R; -import com.android.settings.Settings.RunningServicesActivity; -import com.android.settings.Settings.StorageUseActivity; +import com.android.settings.Settings.AllApplicationsActivity; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.applications.ApplicationsState.AppEntry; -import com.android.settings.deviceinfo.StorageMeasurement; +import com.android.settings.applications.ApplicationsState.AppFilter; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; -import java.util.List; final class CanBeOnSdCardChecker { final IPackageManager mPm; int mInstallLocation; - + CanBeOnSdCardChecker() { mPm = IPackageManager.Stub.asInterface( ServiceManager.getService("package")); } - + void init() { try { mInstallLocation = mPm.getInstallLocation(); @@ -97,7 +80,7 @@ final class CanBeOnSdCardChecker { return; } } - + boolean check(ApplicationInfo info) { boolean canBe = false; if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { @@ -121,414 +104,407 @@ final class CanBeOnSdCardChecker { } } -interface AppClickListener { - void onItemClick(ManageApplications.TabInfo tab, AdapterView parent, - View view, int position, long id); -} - /** * Activity to pick an application that will be used to display installation information and * options to uninstall/delete user data for system applications. This activity * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE * intent. */ -public class ManageApplications extends Fragment implements - AppClickListener, DialogInterface.OnClickListener, DialogInterface.OnDismissListener { +public class ManageApplications extends Fragment implements OnItemClickListener, + OnItemSelectedListener { static final String TAG = "ManageApplications"; - static final boolean DEBUG = false; + static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final String EXTRA_LIST_TYPE = "currentListType"; private static final String EXTRA_SORT_ORDER = "sortOrder"; - private static final String EXTRA_SHOW_BACKGROUND = "showBackground"; - private static final String EXTRA_DEFAULT_LIST_TYPE = "defaultListType"; - private static final String EXTRA_RESET_DIALOG = "resetDialog"; // attributes used as keys when passing values to InstalledAppDetails activity public static final String APP_CHG = "chg"; // constant value that can be used to check return code from sub activity. private static final int INSTALLED_APP_DETAILS = 1; + private static final int ADVANCED_SETTINGS = 2; public static final int SIZE_TOTAL = 0; public static final int SIZE_INTERNAL = 1; public static final int SIZE_EXTERNAL = 2; + // Filter options used for displayed list of applications + // The order which they appear is the order they will show when spinner is present. + public static final int FILTER_APPS_DOWNLOADED_AND_LAUNCHER = 0; + public static final int FILTER_APPS_ALL = 1; + public static final int FILTER_APPS_ENABLED = 2; + public static final int FILTER_APPS_DISABLED = 3; + public static final int FILTER_APPS_PERSONAL = 4; + public static final int FILTER_APPS_WORK = 5; + + // This is the string labels for the filter modes above, the order must be kept in sync. + public static final int[] FILTER_LABELS = new int[] { + R.string.filter_all_apps, // Downloaded and launcher, spinner not shown in this case + R.string.filter_all_apps, // All apps + R.string.filter_enabled_apps, // Enabled + R.string.filter_apps_disabled, // Disabled + R.string.filter_personal_apps, // Personal + R.string.filter_work_apps, // Work + }; + // This is the actual mapping to filters from FILTER_ constants above, the order must + // be kept in sync. + public static final AppFilter[] FILTERS = new AppFilter[] { + ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER, // Downloaded and launcher + ApplicationsState.FILTER_EVERYTHING, // All apps + ApplicationsState.FILTER_ALL_ENABLED, // Enabled + ApplicationsState.FILTER_DISABLED, // Disabled + ApplicationsState.FILTER_PERSONAL, // Personal + ApplicationsState.FILTER_WORK, // Work + }; + // sort order that can be changed through the menu can be sorted alphabetically // or size(descending) private static final int MENU_OPTIONS_BASE = 0; - // Filter options used for displayed list of applications - public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0; - public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1; - public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2; - public static final int FILTER_APPS_DISABLED = MENU_OPTIONS_BASE + 3; - - public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4; - public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5; - public static final int SHOW_RUNNING_SERVICES = MENU_OPTIONS_BASE + 6; - public static final int SHOW_BACKGROUND_PROCESSES = MENU_OPTIONS_BASE + 7; - public static final int RESET_APP_PREFERENCES = MENU_OPTIONS_BASE + 8; + public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 1; + public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 2; + public static final int RESET_APP_PREFERENCES = MENU_OPTIONS_BASE + 3; // sort order private int mSortOrder = SORT_ORDER_ALPHA; - + private ApplicationsState mApplicationsState; - public static class TabInfo implements OnItemClickListener { - public final ManageApplications mOwner; - public final ApplicationsState mApplicationsState; - public final CharSequence mLabel; - public final int mListType; - public final int mFilter; - public final AppClickListener mClickListener; - public final CharSequence mInvalidSizeStr; - public final CharSequence mComputingSizeStr; - private final Bundle mSavedInstanceState; + public int mListType; + public int mFilter; - public ApplicationsAdapter mApplications; - public LayoutInflater mInflater; - public View mRootView; + public ApplicationsAdapter mApplications; - private IMediaContainerService mContainerService; + private View mLoadingContainer; - private View mLoadingContainer; + private View mListContainer; - private View mListContainer; - - // ListView used to display list - private ListView mListView; - // Custom view used to display running processes - private RunningProcessesView mRunningProcessesView; - - //private LinearColorBar mColorBar; - //private TextView mStorageChartLabel; - //private TextView mUsedStorageText; - //private TextView mFreeStorageText; - private long mFreeStorage = 0, mAppStorage = 0, mTotalStorage = 0; - private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage; - - final Runnable mRunningProcessesAvail = new Runnable() { - public void run() { - handleRunningProcessesAvail(); - } - }; - - public TabInfo(ManageApplications owner, ApplicationsState apps, - CharSequence label, int listType, AppClickListener clickListener, - Bundle savedInstanceState) { - mOwner = owner; - mApplicationsState = apps; - mLabel = label; - mListType = listType; - switch (listType) { - case LIST_TYPE_DOWNLOADED: mFilter = FILTER_APPS_THIRD_PARTY; break; - case LIST_TYPE_SDCARD: mFilter = FILTER_APPS_SDCARD; break; - case LIST_TYPE_DISABLED: mFilter = FILTER_APPS_DISABLED; break; - default: mFilter = FILTER_APPS_ALL; break; - } - mClickListener = clickListener; - mInvalidSizeStr = owner.getActivity().getText(R.string.invalid_size_value); - mComputingSizeStr = owner.getActivity().getText(R.string.computing_size); - mSavedInstanceState = savedInstanceState; - } - - public void setContainerService(IMediaContainerService containerService) { - mContainerService = containerService; - updateStorageUsage(); - } - - public View build(LayoutInflater inflater, ViewGroup contentParent, View contentChild) { - if (mRootView != null) { - return mRootView; - } - mInflater = inflater; - mRootView = inflater.inflate(mListType == LIST_TYPE_RUNNING - ? R.layout.manage_applications_running - : R.layout.manage_applications_apps, null); - mLoadingContainer = mRootView.findViewById(R.id.loading_container); - mLoadingContainer.setVisibility(View.VISIBLE); - mListContainer = mRootView.findViewById(R.id.list_container); - if (mListContainer != null) { - // Create adapter and list view here - View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty); - ListView lv = (ListView) mListContainer.findViewById(android.R.id.list); - if (emptyView != null) { - lv.setEmptyView(emptyView); - } - lv.setOnItemClickListener(this); - lv.setSaveEnabled(true); - lv.setItemsCanFocus(true); - lv.setTextFilterEnabled(true); - mListView = lv; - mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter); - mListView.setAdapter(mApplications); - mListView.setRecyclerListener(mApplications); - //mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar); - //mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel); - //mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText); - //mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText); - Utils.prepareCustomPreferencesList(contentParent, contentChild, mListView, false); - if (mFilter == FILTER_APPS_SDCARD) { - //mStorageChartLabel.setText(mOwner.getActivity().getText( - // R.string.sd_card_storage)); - } else { - //mStorageChartLabel.setText(mOwner.getActivity().getText( - // R.string.internal_storage)); - } - applyCurrentStorage(); - } - mRunningProcessesView = (RunningProcessesView)mRootView.findViewById( - R.id.running_processes); - if (mRunningProcessesView != null) { - mRunningProcessesView.doCreate(mSavedInstanceState); - } - - return mRootView; - } - - public void detachView() { - if (mRootView != null) { - ViewGroup group = (ViewGroup)mRootView.getParent(); - if (group != null) { - group.removeView(mRootView); - } - } - } - - public void resume(int sortOrder) { - if (mApplications != null) { - mApplications.resume(sortOrder); - } - if (mRunningProcessesView != null) { - boolean haveData = mRunningProcessesView.doResume(mOwner, mRunningProcessesAvail); - if (haveData) { - mRunningProcessesView.setVisibility(View.VISIBLE); - mLoadingContainer.setVisibility(View.INVISIBLE); - } else { - mLoadingContainer.setVisibility(View.VISIBLE); - } - } - } - - public void pause() { - if (mApplications != null) { - mApplications.pause(); - } - if (mRunningProcessesView != null) { - mRunningProcessesView.doPause(); - } - } - - public void release() { - if (mApplications != null) { - mApplications.release(); - } - } - - void updateStorageUsage() { - // Make sure a callback didn't come at an inopportune time. - if (mOwner.getActivity() == null) return; - // Doesn't make sense for stuff that is not an app list. - if (mApplications == null) return; - - mFreeStorage = 0; - mAppStorage = 0; - mTotalStorage = 0; - - if (mFilter == FILTER_APPS_SDCARD) { - if (mContainerService != null) { - try { - final long[] stats = mContainerService.getFileSystemStats( - Environment.getExternalStorageDirectory().getPath()); - mTotalStorage = stats[0]; - mFreeStorage = stats[1]; - } catch (RemoteException e) { - Log.w(TAG, "Problem in container service", e); - } - } - - if (mApplications != null) { - final int N = mApplications.getCount(); - for (int i=0; i 0) { - BidiFormatter bidiFormatter = BidiFormatter.getInstance(); - mColorBar.setRatios((mTotalStorage-mFreeStorage-mAppStorage)/(float)mTotalStorage, - mAppStorage/(float)mTotalStorage, mFreeStorage/(float)mTotalStorage); - long usedStorage = mTotalStorage - mFreeStorage; - if (mLastUsedStorage != usedStorage) { - mLastUsedStorage = usedStorage; - String sizeStr = bidiFormatter.unicodeWrap( - Formatter.formatShortFileSize(mOwner.getActivity(), usedStorage)); - mUsedStorageText.setText(mOwner.getActivity().getResources().getString( - R.string.service_foreground_processes, sizeStr)); - } - if (mLastFreeStorage != mFreeStorage) { - mLastFreeStorage = mFreeStorage; - String sizeStr = bidiFormatter.unicodeWrap( - Formatter.formatShortFileSize(mOwner.getActivity(), mFreeStorage)); - mFreeStorageText.setText(mOwner.getActivity().getResources().getString( - R.string.service_background_processes, sizeStr)); - } - } else { - mColorBar.setRatios(0, 0, 0); - if (mLastUsedStorage != -1) { - mLastUsedStorage = -1; - mUsedStorageText.setText(""); - } - if (mLastFreeStorage != -1) { - mLastFreeStorage = -1; - mFreeStorageText.setText(""); - } - } - */ - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - mClickListener.onItemClick(this, parent, view, position, id); - } - - void handleRunningProcessesAvail() { - mLoadingContainer.startAnimation(AnimationUtils.loadAnimation( - mOwner.getActivity(), android.R.anim.fade_out)); - mRunningProcessesView.startAnimation(AnimationUtils.loadAnimation( - mOwner.getActivity(), android.R.anim.fade_in)); - mRunningProcessesView.setVisibility(View.VISIBLE); - mLoadingContainer.setVisibility(View.GONE); - } - } - private final ArrayList mTabs = new ArrayList(); - private int mNumTabs; - TabInfo mCurTab = null; + // ListView used to display list + private ListView mListView; // Size resource used for packages whose size computation failed for some reason CharSequence mInvalidSizeStr; - private CharSequence mComputingSizeStr; - + // layout inflater object used to inflate views private LayoutInflater mInflater; - + private String mCurrentPkgName; private int mCurrentUid; - + private Menu mOptionsMenu; - // These are for keeping track of activity and spinner switch state. - private boolean mActivityResumed; + public static final int LIST_TYPE_MAIN = 0; + public static final int LIST_TYPE_ALL = 1; - private static final int LIST_TYPE_MISSING = -1; - static final int LIST_TYPE_DOWNLOADED = 0; - static final int LIST_TYPE_RUNNING = 1; - static final int LIST_TYPE_SDCARD = 2; - static final int LIST_TYPE_ALL = 3; - static final int LIST_TYPE_DISABLED = 4; - - private boolean mShowBackground = false; - - private int mDefaultListType = -1; - - private ViewGroup mContentContainer; private View mRootView; - private ViewPager mViewPager; - private ViewGroup mPinnedHeader; - private Spinner mSpinner; - private Context mContext; - AlertDialog mResetDialog; + private View mSpinnerHeader; + private Spinner mFilterSpinner; + private FilterSpinnerAdapter mFilterAdapter; - class MyPagerAdapter extends PagerAdapter - implements ViewPager.OnPageChangeListener { - int mCurPos = 0; + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); + + Intent intent = getActivity().getIntent(); + String action = intent.getAction(); + String className = getArguments() != null + ? getArguments().getString("classname") : null; + if (className == null) { + className = intent.getComponent().getClassName(); + } + if (Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action) + || className.equals(AllApplicationsActivity.class.getName())) { + mListType = LIST_TYPE_ALL; + } else { + mListType = LIST_TYPE_MAIN; + } + mFilter = getDefaultFilter(); + + if (savedInstanceState != null) { + mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder); + } + + mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value); + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // initialize the inflater + mInflater = inflater; + + createHeader(); + + mRootView = inflater.inflate(R.layout.manage_applications_apps, null); + mLoadingContainer = mRootView.findViewById(R.id.loading_container); + mLoadingContainer.setVisibility(View.VISIBLE); + mListContainer = mRootView.findViewById(R.id.list_container); + if (mListContainer != null) { + // Create adapter and list view here + View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty); + ListView lv = (ListView) mListContainer.findViewById(android.R.id.list); + if (emptyView != null) { + lv.setEmptyView(emptyView); + } + lv.setOnItemClickListener(this); + lv.setSaveEnabled(true); + lv.setItemsCanFocus(true); + lv.setTextFilterEnabled(true); + mListView = lv; + mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter); + mListView.setAdapter(mApplications); + mListView.setRecyclerListener(mApplications); + + Utils.prepareCustomPreferencesList(container, mRootView, mListView, false); + } + + // We have to do this now because PreferenceFrameLayout looks at it + // only when the view is added. + if (container instanceof PreferenceFrameLayout) { + ((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true; + } + + return mRootView; + } + + private void createHeader() { + Activity activity = getActivity(); + View content = activity.findViewById(R.id.main_content); + ViewGroup contentParent = (ViewGroup) content.getParent(); + mSpinnerHeader = (ViewGroup) activity.getLayoutInflater() + .inflate(R.layout.apps_filter_spinner, contentParent, false); + mFilterSpinner = (Spinner) mSpinnerHeader.findViewById(R.id.filter_spinner); + mFilterAdapter = new FilterSpinnerAdapter(this); + mFilterSpinner.setAdapter(mFilterAdapter); + mFilterSpinner.setOnItemSelectedListener(this); + contentParent.addView(mSpinnerHeader, 0); + + mFilterAdapter.enableFilter(getDefaultFilter()); + if (mListType != LIST_TYPE_MAIN) { + if (UserManager.get(getActivity()).getUserProfiles().size() > 1) { + mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL); + mFilterAdapter.enableFilter(FILTER_APPS_WORK); + } + } + } + + private int getDefaultFilter() { + if (mListType == LIST_TYPE_MAIN) { + return FILTER_APPS_DOWNLOADED_AND_LAUNCHER; + } + return FILTER_APPS_ALL; + } + + @Override + public void onResume() { + super.onResume(); + updateView(); + updateOptionsMenu(); + if (mApplications != null) { + mApplications.resume(mSortOrder); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(EXTRA_SORT_ORDER, mSortOrder); + } + + @Override + public void onPause() { + super.onPause(); + if (mApplications != null) { + mApplications.pause(); + } + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + if (mApplications != null) { + mApplications.release(); + } + mRootView = null; + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { + mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid)); + } + } + + // utility method used to start sub activity + private void startApplicationDetailsActivity() { + // TODO: Figure out if there is a way where we can spin up the profile's settings + // process ahead of time, to avoid a long load of data when user clicks on a managed app. + // Maybe when they load the list of apps that contains managed profile apps. + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", mCurrentPkgName, null)); + getActivity().startActivityAsUser(intent, + new UserHandle(UserHandle.getUserId(mCurrentUid))); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + mOptionsMenu = menu; + if (mListType == LIST_TYPE_MAIN) { + // Only show advanced options when in the main app list (from dashboard). + inflater.inflate(R.menu.manage_apps, menu); + } + menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + updateOptionsMenu(); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + updateOptionsMenu(); + } + + @Override + public void onDestroyOptionsMenu() { + mOptionsMenu = null; + } + + void updateOptionsMenu() { + if (mOptionsMenu == null) { + return; + } + + if (mListType != LIST_TYPE_MAIN) { + // Allow sorting except on main apps list. + mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA); + mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE); + } else { + mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(false); + mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(false); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int menuId = item.getItemId(); + switch(item.getItemId()) { + case SORT_ORDER_ALPHA: + case SORT_ORDER_SIZE: + mSortOrder = menuId; + if (mApplications != null) { + mApplications.rebuild(mSortOrder); + } + break; + case R.id.advanced: + ((SettingsActivity) getActivity()).startPreferencePanel( + AdvancedAppSettings.class.getName(), null, R.string.advanced_apps, + null, this, ADVANCED_SETTINGS); + return true; + default: + // Handle the home button + return false; + } + updateOptionsMenu(); + return true; + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (mApplications != null && mApplications.getCount() > position) { + ApplicationsState.AppEntry entry = mApplications.getAppEntry(position); + mCurrentPkgName = entry.info.packageName; + mCurrentUid = entry.info.uid; + startApplicationDetailsActivity(); + } + } + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + mApplications.setFilter(mFilterAdapter.getFilter(position)); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + + public void updateView() { + updateOptionsMenu(); + final Activity host = getActivity(); + if (host != null) { + host.invalidateOptionsMenu(); + } + } + + public void setHasDisabled(boolean hasDisabledApps) { + if (mListType == LIST_TYPE_MAIN) { + // No filtering on main app list. + return; + } + if (hasDisabledApps) { + mFilterAdapter.enableFilter(FILTER_APPS_ENABLED); + mFilterAdapter.enableFilter(FILTER_APPS_DISABLED); + } else { + mFilterAdapter.disableFilter(FILTER_APPS_ENABLED); + mFilterAdapter.disableFilter(FILTER_APPS_DISABLED); + } + } + + static class FilterSpinnerAdapter extends ArrayAdapter { + + private final ManageApplications mManageApplications; + + // Use ArrayAdapter for view logic, but have our own list for managing + // the options available. + private final ArrayList mFilterOptions = new ArrayList<>(); + + public FilterSpinnerAdapter(ManageApplications manageApplications) { + super(manageApplications.getActivity(), R.layout.filter_spinner_item); + setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mManageApplications = manageApplications; + } + + public int getFilter(int position) { + return mFilterOptions.get(position); + } + + public void enableFilter(int filter) { + if (mFilterOptions.contains(filter)) return; + mFilterOptions.add(filter); + Collections.sort(mFilterOptions); + mManageApplications.mSpinnerHeader.setVisibility( + mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE); + notifyDataSetChanged(); + } + + public void disableFilter(int filter) { + if (!mFilterOptions.remove((Integer) filter)) { + return; + } + Collections.sort(mFilterOptions); + mManageApplications.mSpinnerHeader.setVisibility( + mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE); + notifyDataSetChanged(); + } @Override public int getCount() { - return mNumTabs; - } - - @Override - public Object instantiateItem(ViewGroup container, int position) { - TabInfo tab = mTabs.get(position); - View root = tab.build(mInflater, mContentContainer, mRootView); - container.addView(root); - root.setTag(R.id.name, tab); - return root; + return mFilterOptions.size(); } @Override - public void destroyItem(ViewGroup container, int position, Object object) { - container.removeView((View)object); + public CharSequence getItem(int position) { + return getFilterString(mFilterOptions.get(position)); } - @Override - public boolean isViewFromObject(View view, Object object) { - return view == object; + private CharSequence getFilterString(int filter) { + return mManageApplications.getString(FILTER_LABELS[filter]); } - @Override - public int getItemPosition(Object object) { - return super.getItemPosition(object); - //return ((TabInfo)((View)object).getTag(R.id.name)).mListType; - } - - @Override - public CharSequence getPageTitle(int position) { - return mTabs.get(position).mLabel; - } - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - } - - @Override - public void onPageSelected(int position) { - mCurPos = position; - } - - @Override - public void onPageScrollStateChanged(int state) { - if (state == ViewPager.SCROLL_STATE_IDLE) { - updateCurrentTab(mCurPos); - } - } } /* @@ -544,15 +520,14 @@ public class ManageApplications extends Fragment implements ApplicationsState.Callbacks, AbsListView.RecyclerListener { private final ApplicationsState mState; private final ApplicationsState.Session mSession; - private final TabInfo mTab; + private final ManageApplications mManageApplications; private final Context mContext; private final ArrayList mActive = new ArrayList(); - private final int mFilterMode; + private int mFilterMode; private ArrayList mBaseEntries; private ArrayList mEntries; private boolean mResumed; private int mLastSortMode=-1; - private boolean mWaitingForData; private int mWhichSize = SIZE_TOTAL; CharSequence mCurFilterPrefix; @@ -568,22 +543,28 @@ public class ManageApplications extends Fragment implements } @Override + @SuppressWarnings("unchecked") protected void publishResults(CharSequence constraint, FilterResults results) { mCurFilterPrefix = constraint; - mEntries = (ArrayList)results.values; + mEntries = (ArrayList) results.values; notifyDataSetChanged(); - mTab.updateStorageUsage(); } }; - public ApplicationsAdapter(ApplicationsState state, TabInfo tab, int filterMode) { + public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications, + int filterMode) { mState = state; mSession = state.newSession(this); - mTab = tab; - mContext = tab.mOwner.getActivity(); + mManageApplications = manageApplications; + mContext = manageApplications.getActivity(); mFilterMode = filterMode; } + public void setFilter(int filter) { + mFilterMode = filter; + rebuild(true); + } + public void resume(int sort) { if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed); if (!mResumed) { @@ -614,7 +595,7 @@ public class ManageApplications extends Fragment implements mLastSortMode = sort; rebuild(true); } - + public void rebuild(boolean eraseold) { if (DEBUG) Log.i(TAG, "Rebuilding app list..."); ApplicationsState.AppFilter filterObj; @@ -625,23 +606,7 @@ public class ManageApplications extends Fragment implements } else { mWhichSize = SIZE_INTERNAL; } - switch (mFilterMode) { - case FILTER_APPS_THIRD_PARTY: - filterObj = ApplicationsState.THIRD_PARTY_FILTER; - break; - case FILTER_APPS_SDCARD: - filterObj = ApplicationsState.ON_SD_CARD_FILTER; - if (!emulated) { - mWhichSize = SIZE_EXTERNAL; - } - break; - case FILTER_APPS_DISABLED: - filterObj = ApplicationsState.DISABLED_FILTER; - break; - default: - filterObj = ApplicationsState.ALL_ENABLED_FILTER; - break; - } + filterObj = FILTERS[mFilterMode]; switch (mLastSortMode) { case SORT_ORDER_SIZE: switch (mWhichSize) { @@ -673,16 +638,26 @@ public class ManageApplications extends Fragment implements mEntries = null; } notifyDataSetChanged(); - mTab.updateStorageUsage(); if (entries == null) { - mWaitingForData = true; - mTab.mListContainer.setVisibility(View.INVISIBLE); - mTab.mLoadingContainer.setVisibility(View.VISIBLE); + mManageApplications.mListContainer.setVisibility(View.INVISIBLE); + mManageApplications.mLoadingContainer.setVisibility(View.VISIBLE); } else { - mTab.mListContainer.setVisibility(View.VISIBLE); - mTab.mLoadingContainer.setVisibility(View.GONE); + mManageApplications.mListContainer.setVisibility(View.VISIBLE); + mManageApplications.mLoadingContainer.setVisibility(View.GONE); } + + mManageApplications.setHasDisabled(hasDisabledApps()); + } + + private boolean hasDisabledApps() { + ArrayList allApps = mSession.getAllApps(); + for (int i = 0; i < allApps.size(); i++) { + if (!allApps.get(i).info.enabled) { + return true; + } + } + return false; } ArrayList applyPrefixFilter(CharSequence prefix, @@ -707,24 +682,22 @@ public class ManageApplications extends Fragment implements @Override public void onRunningStateChanged(boolean running) { - mTab.mOwner.getActivity().setProgressBarIndeterminateVisibility(running); + mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running); } @Override public void onRebuildComplete(ArrayList apps) { - if (mTab.mLoadingContainer.getVisibility() == View.VISIBLE) { - mTab.mLoadingContainer.startAnimation(AnimationUtils.loadAnimation( + if (mManageApplications.mLoadingContainer.getVisibility() == View.VISIBLE) { + mManageApplications.mLoadingContainer.startAnimation(AnimationUtils.loadAnimation( mContext, android.R.anim.fade_out)); - mTab.mListContainer.startAnimation(AnimationUtils.loadAnimation( + mManageApplications.mListContainer.startAnimation(AnimationUtils.loadAnimation( mContext, android.R.anim.fade_in)); } - mTab.mListContainer.setVisibility(View.VISIBLE); - mTab.mLoadingContainer.setVisibility(View.GONE); - mWaitingForData = false; + mManageApplications.mListContainer.setVisibility(View.VISIBLE); + mManageApplications.mLoadingContainer.setVisibility(View.GONE); mBaseEntries = apps; mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries); notifyDataSetChanged(); - mTab.updateStorageUsage(); } @Override @@ -744,9 +717,9 @@ public class ManageApplications extends Fragment implements AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag(); if (holder.entry.info.packageName.equals(packageName)) { synchronized (holder.entry) { - holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize); + holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize); } - if (holder.entry.info.packageName.equals(mTab.mOwner.mCurrentPkgName) + if (holder.entry.info.packageName.equals(mManageApplications.mCurrentPkgName) && mLastSortMode == SORT_ORDER_SIZE) { // We got the size information for the last app the // user viewed, and are sorting by size... they may @@ -754,28 +727,33 @@ public class ManageApplications extends Fragment implements // the list with the new size to reflect it to the user. rebuild(false); } - mTab.updateStorageUsage(); return; } } } + @Override + public void onLauncherInfoChanged() { + if (mFilterMode == FILTER_APPS_DOWNLOADED_AND_LAUNCHER) { + rebuild(false); + } + } + @Override public void onAllSizesComputed() { if (mLastSortMode == SORT_ORDER_SIZE) { rebuild(false); } - mTab.updateStorageUsage(); } - + public int getCount() { return mEntries != null ? mEntries.size() : 0; } - + public Object getItem(int position) { return mEntries.get(position); } - + public ApplicationsState.AppEntry getAppEntry(int position) { return mEntries.get(position); } @@ -783,11 +761,12 @@ public class ManageApplications extends Fragment implements public long getItemId(int position) { return mEntries.get(position).id; } - + public View getView(int position, View convertView, ViewGroup parent) { // A ViewHolder keeps references to children views to avoid unnecessary calls // to findViewById() on each row. - AppViewHolder holder = AppViewHolder.createOrRecycle(mTab.mInflater, convertView); + AppViewHolder holder = AppViewHolder.createOrRecycle(mManageApplications.mInflater, + convertView); convertView = holder.rootView; // Bind the data efficiently with the holder @@ -801,7 +780,7 @@ public class ManageApplications extends Fragment implements if (entry.icon != null) { holder.appIcon.setImageDrawable(entry.icon); } - holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize); + holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize); if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { holder.disabled.setVisibility(View.VISIBLE); holder.disabled.setText(R.string.not_installed); @@ -811,13 +790,7 @@ public class ManageApplications extends Fragment implements } else { holder.disabled.setVisibility(View.GONE); } - if (mFilterMode == FILTER_APPS_SDCARD) { - holder.checkBox.setVisibility(View.VISIBLE); - holder.checkBox.setChecked((entry.info.flags - & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); - } else { - holder.checkBox.setVisibility(View.GONE); - } + holder.checkBox.setVisibility(View.GONE); } mActive.remove(convertView); mActive.add(convertView); @@ -834,463 +807,4 @@ public class ManageApplications extends Fragment implements mActive.remove(view); } } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setHasOptionsMenu(true); - - mContext = getActivity(); - mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); - Intent intent = getActivity().getIntent(); - String action = intent.getAction(); - int defaultListType = LIST_TYPE_DOWNLOADED; - String className = getArguments() != null - ? getArguments().getString("classname") : null; - if (className == null) { - className = intent.getComponent().getClassName(); - } - if (className.equals(RunningServicesActivity.class.getName()) - || className.endsWith(".RunningServices")) { - defaultListType = LIST_TYPE_RUNNING; - } else if (className.equals(StorageUseActivity.class.getName()) - || Intent.ACTION_MANAGE_PACKAGE_STORAGE.equals(action) - || className.endsWith(".StorageUse")) { - mSortOrder = SORT_ORDER_SIZE; - defaultListType = LIST_TYPE_ALL; - } else if (android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) { - // Select the all-apps list, with the default sorting - defaultListType = LIST_TYPE_ALL; - } - - if (savedInstanceState != null) { - mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder); - int tmp = savedInstanceState.getInt(EXTRA_DEFAULT_LIST_TYPE, -1); - if (tmp != -1) defaultListType = tmp; - mShowBackground = savedInstanceState.getBoolean(EXTRA_SHOW_BACKGROUND, false); - } - - mDefaultListType = defaultListType; - - final Intent containerIntent = new Intent().setComponent( - StorageMeasurement.DEFAULT_CONTAINER_COMPONENT); - getActivity().bindService(containerIntent, mContainerConnection, Context.BIND_AUTO_CREATE); - - mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value); - mComputingSizeStr = getActivity().getText(R.string.computing_size); - - TabInfo tab = new TabInfo(this, mApplicationsState, - getActivity().getString(R.string.filter_apps_third_party), - LIST_TYPE_DOWNLOADED, this, savedInstanceState); - mTabs.add(tab); - - if (!Environment.isExternalStorageEmulated()) { - tab = new TabInfo(this, mApplicationsState, - getActivity().getString(R.string.filter_apps_onsdcard), - LIST_TYPE_SDCARD, this, savedInstanceState); - mTabs.add(tab); - } - - tab = new TabInfo(this, mApplicationsState, - getActivity().getString(R.string.filter_apps_running), - LIST_TYPE_RUNNING, this, savedInstanceState); - mTabs.add(tab); - - tab = new TabInfo(this, mApplicationsState, - getActivity().getString(R.string.filter_apps_all), - LIST_TYPE_ALL, this, savedInstanceState); - mTabs.add(tab); - - tab = new TabInfo(this, mApplicationsState, - getActivity().getString(R.string.filter_apps_disabled), - LIST_TYPE_DISABLED, this, savedInstanceState); - mTabs.add(tab); - - mNumTabs = mTabs.size(); - } - - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // initialize the inflater - mInflater = inflater; - - View rootView = mInflater.inflate(R.layout.manage_applications_content, - container, false); - mContentContainer = container; - mRootView = rootView; - mViewPager = (ViewPager) rootView.findViewById(R.id.pager); - MyPagerAdapter adapter = new MyPagerAdapter(); - mViewPager.setAdapter(adapter); - mViewPager.setOnPageChangeListener(adapter); - PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs); - - // This should be set in the XML layout, but PagerTabStrip lives in - // support-v4 and doesn't have styleable attributes. - final TypedArray ta = tabs.getContext().obtainStyledAttributes( - new int[] { android.R.attr.colorAccent }); - final int colorAccent = ta.getColor(0, 0); - ta.recycle(); - - tabs.setTabIndicatorColorResource(colorAccent); - - // We have to do this now because PreferenceFrameLayout looks at it - // only when the view is added. - if (container instanceof PreferenceFrameLayout) { - ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true; - } - - if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_RESET_DIALOG)) { - buildResetDialog(); - } - - if (savedInstanceState == null) { - // First time init: make sure view pager is showing the correct tab. - int extraCurrentListType = getActivity().getIntent().getIntExtra(EXTRA_LIST_TYPE, - LIST_TYPE_MISSING); - int currentListType = (extraCurrentListType != LIST_TYPE_MISSING) - ? extraCurrentListType : mDefaultListType; - for (int i = 0; i < mNumTabs; i++) { - TabInfo tab = mTabs.get(i); - if (tab.mListType == currentListType) { - mViewPager.setCurrentItem(i); - break; - } - } - } - - return rootView; - } - - @Override - public void onStart() { - super.onStart(); - } - - @Override - public void onResume() { - super.onResume(); - mActivityResumed = true; - updateCurrentTab(mViewPager.getCurrentItem()); - updateNumTabs(); - updateOptionsMenu(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putInt(EXTRA_SORT_ORDER, mSortOrder); - if (mDefaultListType != -1) { - outState.putInt(EXTRA_DEFAULT_LIST_TYPE, mDefaultListType); - } - outState.putBoolean(EXTRA_SHOW_BACKGROUND, mShowBackground); - if (mResetDialog != null) { - outState.putBoolean(EXTRA_RESET_DIALOG, true); - } - } - - @Override - public void onPause() { - super.onPause(); - mActivityResumed = false; - for (int i=0; i() { - @Override protected Void doInBackground(Void... params) { - List apps = pm.getInstalledApplications( - PackageManager.GET_DISABLED_COMPONENTS); - for (int i=0; i parent, View view, int position, - long id) { - if (tab.mApplications != null && tab.mApplications.getCount() > position) { - ApplicationsState.AppEntry entry = tab.mApplications.getAppEntry(position); - mCurrentPkgName = entry.info.packageName; - mCurrentUid = entry.info.uid; - startApplicationDetailsActivity(); - } - } - - public void updateCurrentTab(int position) { - TabInfo tab = mTabs.get(position); - mCurTab = tab; - - // Put things in the correct paused/resumed state. - if (mActivityResumed) { - mCurTab.build(mInflater, mContentContainer, mRootView); - mCurTab.resume(mSortOrder); - } else { - mCurTab.pause(); - } - for (int i=0; i