summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2010-07-27 14:41:34 -0700
committerDianne Hackborn <hackbod@google.com>2010-07-27 15:23:51 -0700
commite7623f775cb37a8de049dae8c03b5b396526fdd1 (patch)
treec8c96fcddf6d9951ed5f27037194cbee6fe2e868
parent938079c9b36f0a45e58b5b086393647d71aebeba (diff)
downloadpackages_apps_Settings-e7623f775cb37a8de049dae8c03b5b396526fdd1.zip
packages_apps_Settings-e7623f775cb37a8de049dae8c03b5b396526fdd1.tar.gz
packages_apps_Settings-e7623f775cb37a8de049dae8c03b5b396526fdd1.tar.bz2
Rewrite Manage Applications to be faster and simpler.
Change-Id: I4cf405e83a369cb935bccf375798557ffd93e14e
-rw-r--r--AndroidManifest.xml1
-rw-r--r--src/com/android/settings/applications/ApplicationsState.java567
-rw-r--r--src/com/android/settings/applications/ManageApplications.java1942
-rw-r--r--src/com/android/settings/applications/RunningProcessesView.java4
4 files changed, 796 insertions, 1718 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 13005e4..43374eb 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -353,7 +353,6 @@
<activity android:name=".applications.ManageApplications"
android:label="@string/manageapplications_settings_title"
android:clearTaskOnLaunch="true"
- android:configChanges="orientation|keyboardHidden"
android:theme="@android:style/Theme.NoTitleBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java
new file mode 100644
index 0000000..d59aadd
--- /dev/null
+++ b/src/com/android/settings/applications/ApplicationsState.java
@@ -0,0 +1,567 @@
+package com.android.settings.applications;
+
+import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.text.format.Formatter;
+import android.util.Log;
+
+import java.text.Collator;
+import java.text.Normalizer;
+import java.text.Normalizer.Form;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Keeps track of information about all installed applications, lazy-loading
+ * as needed.
+ */
+public class ApplicationsState {
+ static final String TAG = "ApplicationsState";
+ static final boolean DEBUG = false;
+
+ public static interface Callbacks {
+ public void onRunningStateChanged(boolean running);
+ public void onPackageListChanged();
+ public void onPackageIconChanged();
+ public void onPackageSizeChanged(String packageName);
+ public void onAllSizesComputed();
+ }
+
+ public static interface AppFilter {
+ public boolean filterApp(ApplicationInfo info);
+ }
+
+ static final int SIZE_UNKNOWN = -1;
+ static final int SIZE_INVALID = -2;
+
+ static final Pattern REMOVE_DIACRITICALS_PATTERN
+ = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
+
+ public static String normalize(String str) {
+ String tmp = Normalizer.normalize(str, Form.NFD);
+ return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
+ .replaceAll("").toLowerCase();
+ }
+
+ public static class AppEntry {
+ final String label;
+ final long id;
+ long size;
+
+ String getNormalizedLabel() {
+ if (normalizedLabel != null) {
+ return normalizedLabel;
+ }
+ normalizedLabel = normalize(label);
+ return normalizedLabel;
+ }
+
+ // Need to synchronize on 'this' for the following.
+ ApplicationInfo info;
+ Drawable icon;
+ String sizeStr;
+ boolean sizeStale;
+ long sizeLoadStart;
+
+ String normalizedLabel;
+
+ AppEntry(Context context, ApplicationInfo info, long id) {
+ CharSequence label = info.loadLabel(context.getPackageManager());
+ this.label = label != null ? label.toString() : info.packageName;
+ this.id = id;
+ this.info = info;
+ this.size = SIZE_UNKNOWN;
+ this.sizeStale = true;
+ }
+ }
+
+ public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
+ private final Collator sCollator = Collator.getInstance();
+ @Override
+ public int compare(AppEntry object1, AppEntry object2) {
+ return sCollator.compare(object1.label, object2.label);
+ }
+ };
+
+ public static final Comparator<AppEntry> SIZE_COMPARATOR = new Comparator<AppEntry>() {
+ private final Collator sCollator = Collator.getInstance();
+ @Override
+ public int compare(AppEntry object1, AppEntry object2) {
+ if (object1.size < object2.size) return 1;
+ if (object1.size > object2.size) return -1;
+ return sCollator.compare(object1.label, object2.label);
+ }
+ };
+
+ public static final AppFilter THIRD_PARTY_FILTER = new AppFilter() {
+ @Override
+ public boolean filterApp(ApplicationInfo info) {
+ if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ return true;
+ }
+ return false;
+ }
+ };
+
+ public static final AppFilter ON_SD_CARD_FILTER = new AppFilter() {
+ @Override
+ public boolean filterApp(ApplicationInfo info) {
+ if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ return true;
+ } else if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ return true;
+ }
+ return false;
+ }
+ };
+
+ final Context mContext;
+ final PackageManager mPm;
+ PackageIntentReceiver mPackageIntentReceiver;
+
+ boolean mResumed;
+ Callbacks mCurCallbacks;
+
+ // Information about all applications. Synchronize on mAppEntries
+ // to protect access to these.
+ final HashMap<String, AppEntry> mEntriesMap = new HashMap<String, AppEntry>();
+ final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
+ List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
+ long mCurId = 1;
+ String mCurComputingSizePkg;
+
+ /**
+ * Receives notifications when applications are added/removed.
+ */
+ private class PackageIntentReceiver extends BroadcastReceiver {
+ void registerReceiver() {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(this, filter);
+ // Register for events related to sdcard installation.
+ IntentFilter sdFilter = new IntentFilter();
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ mContext.registerReceiver(this, sdFilter);
+ }
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String actionStr = intent.getAction();
+ if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
+ Uri data = intent.getData();
+ String pkgName = data.getEncodedSchemeSpecificPart();
+ addPackage(pkgName);
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
+ Uri data = intent.getData();
+ String pkgName = data.getEncodedSchemeSpecificPart();
+ removePackage(pkgName);
+ } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
+ Uri data = intent.getData();
+ String pkgName = data.getEncodedSchemeSpecificPart();
+ removePackage(pkgName);
+ addPackage(pkgName);
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
+ Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
+ // When applications become available or unavailable (perhaps because
+ // the SD card was inserted or ejected) we need to refresh the
+ // AppInfo with new label, icon and size information as appropriate
+ // given the newfound (un)availability of the application.
+ // A simple way to do that is to treat the refresh as a package
+ // removal followed by a package addition.
+ String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ if (pkgList == null || pkgList.length == 0) {
+ // Ignore
+ return;
+ }
+ for (String pkgName : pkgList) {
+ removePackage(pkgName);
+ addPackage(pkgName);
+ }
+ }
+ }
+ }
+
+ class MainHandler extends Handler {
+ static final int MSG_PACKAGE_LIST_CHANGED = 1;
+ static final int MSG_PACKAGE_ICON_CHANGED = 2;
+ static final int MSG_PACKAGE_SIZE_CHANGED = 4;
+ static final int MSG_ALL_SIZES_COMPUTED = 5;
+ static final int MSG_RUNNING_STATE_CHANGED = 6;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PACKAGE_LIST_CHANGED: {
+ if (mCurCallbacks != null) {
+ mCurCallbacks.onPackageListChanged();
+ }
+ } break;
+ case MSG_PACKAGE_ICON_CHANGED: {
+ if (mCurCallbacks != null) {
+ mCurCallbacks.onPackageIconChanged();
+ }
+ } break;
+ case MSG_PACKAGE_SIZE_CHANGED: {
+ if (mCurCallbacks != null) {
+ mCurCallbacks.onPackageSizeChanged((String)msg.obj);
+ }
+ } break;
+ case MSG_ALL_SIZES_COMPUTED: {
+ if (mCurCallbacks != null) {
+ mCurCallbacks.onAllSizesComputed();
+ }
+ } break;
+ case MSG_RUNNING_STATE_CHANGED: {
+ if (mCurCallbacks != null) {
+ mCurCallbacks.onRunningStateChanged(msg.arg1 != 0);
+ }
+ } break;
+ }
+ }
+ }
+
+ final MainHandler mMainHandler = new MainHandler();
+
+ // --------------------------------------------------------------
+
+ static final Object sLock = new Object();
+ static ApplicationsState sInstance;
+
+ static ApplicationsState getInstance(Application app) {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new ApplicationsState(app);
+ }
+ return sInstance;
+ }
+ }
+
+ private ApplicationsState(Application app) {
+ mContext = app;
+ mPm = mContext.getPackageManager();
+ mThread = new HandlerThread("ApplicationsState.Loader",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ mThread.start();
+ mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
+ }
+
+ void resume(Callbacks callbacks) {
+ synchronized (mEntriesMap) {
+ mCurCallbacks = callbacks;
+ mResumed = true;
+ if (mPackageIntentReceiver == null) {
+ mPackageIntentReceiver = new PackageIntentReceiver();
+ mPackageIntentReceiver.registerReceiver();
+ }
+ mApplications = mPm.getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES |
+ PackageManager.GET_DISABLED_COMPONENTS);
+ if (mApplications == null) {
+ mApplications = new ArrayList<ApplicationInfo>();
+ }
+ for (int i=0; i<mAppEntries.size(); i++) {
+ mAppEntries.get(i).sizeStale = true;
+ }
+ mCurComputingSizePkg = null;
+ if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
+ mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
+ }
+ }
+ }
+
+ void pause() {
+ synchronized (mEntriesMap) {
+ mCurCallbacks = null;
+ mResumed = false;
+ }
+ }
+
+ // Creates a new list of app entries with the given filter and comparator.
+ ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
+ ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
+ synchronized (mEntriesMap) {
+ if (DEBUG) Log.i(TAG, "Rebuilding...");
+ for (int i=0; i<mApplications.size(); i++) {
+ ApplicationInfo info = mApplications.get(i);
+ if (filter == null || filter.filterApp(info)) {
+ AppEntry entry = getEntryLocked(info);
+ if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
+ filteredApps.add(entry);
+ }
+ }
+ }
+ Collections.sort(filteredApps, comparator);
+ return filteredApps;
+ }
+
+ void ensureIcon(AppEntry entry) {
+ if (entry.icon != null) {
+ return;
+ }
+ synchronized (mEntriesMap) {
+ if (entry.icon == null) {
+ entry.icon = entry.info.loadIcon(mPm);
+ }
+ }
+ }
+
+ void requestSize(String packageName) {
+ synchronized (mEntriesMap) {
+ AppEntry entry = mEntriesMap.get(packageName);
+ if (entry != null) {
+ mPm.getPackageSizeInfo(packageName, mBackgroundHandler.mStatsObserver);
+ }
+ }
+ }
+
+ int indexOfApplicationInfoLocked(String pkgName) {
+ for (int i=mApplications.size()-1; i>=0; i--) {
+ if (mApplications.get(i).packageName.equals(pkgName)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ void addPackage(String pkgName) {
+ try {
+ synchronized (mEntriesMap) {
+ if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
+ if (!mResumed) {
+ // If we are not resumed, we will do a full query the
+ // next time we resume, so there is no reason to do work
+ // here.
+ return;
+ }
+ if (indexOfApplicationInfoLocked(pkgName) >= 0) {
+ if (DEBUG) Log.i(TAG, "Package already exists!");
+ return;
+ }
+ ApplicationInfo info = mPm.getApplicationInfo(pkgName,
+ PackageManager.GET_UNINSTALLED_PACKAGES |
+ PackageManager.GET_DISABLED_COMPONENTS);
+ mApplications.add(info);
+ if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
+ mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
+ }
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
+ }
+ }
+ } catch (NameNotFoundException e) {
+ }
+ }
+
+ void removePackage(String pkgName) {
+ synchronized (mEntriesMap) {
+ int idx = indexOfApplicationInfoLocked(pkgName);
+ if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
+ if (idx >= 0) {
+ AppEntry entry = mEntriesMap.get(pkgName);
+ if (DEBUG) Log.i(TAG, "removePackage: " + entry);
+ if (entry != null) {
+ mEntriesMap.remove(pkgName);
+ mAppEntries.remove(entry);
+ }
+ mApplications.remove(idx);
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
+ }
+ }
+ }
+ }
+
+ AppEntry getEntryLocked(ApplicationInfo info) {
+ AppEntry entry = mEntriesMap.get(info.packageName);
+ if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
+ if (entry == null) {
+ if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName);
+ entry = new AppEntry(mContext, info, mCurId++);
+ mEntriesMap.put(info.packageName, entry);
+ mAppEntries.add(entry);
+ } else if (entry.info != info) {
+ entry.info = info;
+ }
+ return entry;
+ }
+
+ // --------------------------------------------------------------
+
+ private long getTotalSize(PackageStats ps) {
+ if (ps != null) {
+ return ps.cacheSize+ps.codeSize+ps.dataSize;
+ }
+ return SIZE_INVALID;
+ }
+
+ private String getSizeStr(long size) {
+ if (size >= 0) {
+ return Formatter.formatFileSize(mContext, size);
+ }
+ return null;
+ }
+
+ final HandlerThread mThread;
+ final BackgroundHandler mBackgroundHandler;
+ class BackgroundHandler extends Handler {
+ static final int MSG_LOAD_ENTRIES = 1;
+ static final int MSG_LOAD_ICONS = 2;
+ static final int MSG_LOAD_SIZES = 3;
+
+ boolean mRunning;
+
+ final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
+ public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
+ boolean sizeChanged = false;
+ synchronized (mEntriesMap) {
+ AppEntry entry = mEntriesMap.get(stats.packageName);
+ if (entry != null) {
+ synchronized (entry) {
+ entry.sizeStale = false;
+ entry.sizeLoadStart = 0;
+ long newSize = getTotalSize(stats);
+ if (entry.size != newSize) {
+ entry.size = newSize;
+ entry.sizeStr = getSizeStr(entry.size);
+ if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
+ + ": " + entry.sizeStr);
+ sizeChanged = true;
+ }
+ }
+ if (sizeChanged) {
+ Message msg = mMainHandler.obtainMessage(
+ MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
+ mMainHandler.sendMessage(msg);
+ }
+ }
+ if (mCurComputingSizePkg == null
+ || mCurComputingSizePkg.equals(stats.packageName)) {
+ mCurComputingSizePkg = null;
+ sendEmptyMessage(MSG_LOAD_SIZES);
+ }
+ }
+ }
+ };
+
+ BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_LOAD_ENTRIES: {
+ int numDone = 0;
+ synchronized (mEntriesMap) {
+ for (int i=0; i<mApplications.size() && numDone<6; i++) {
+ if (!mRunning) {
+ mRunning = true;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+ mMainHandler.sendMessage(m);
+ }
+ ApplicationInfo info = mApplications.get(i);
+ if (mEntriesMap.get(info.packageName) == null) {
+ numDone++;
+ getEntryLocked(info);
+ }
+ }
+ }
+
+ if (numDone >= 6) {
+ sendEmptyMessage(MSG_LOAD_ENTRIES);
+ } else {
+ sendEmptyMessage(MSG_LOAD_ICONS);
+ }
+ } break;
+ case MSG_LOAD_ICONS: {
+ int numDone = 0;
+ synchronized (mEntriesMap) {
+ for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
+ AppEntry entry = mAppEntries.get(i);
+ if (entry.icon == null) {
+ if (!mRunning) {
+ mRunning = true;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+ mMainHandler.sendMessage(m);
+ }
+ numDone++;
+ synchronized (entry) {
+ entry.icon = entry.info.loadIcon(mPm);
+ }
+ }
+ }
+ }
+ if (numDone > 0) {
+ if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
+ }
+ }
+ if (numDone >= 2) {
+ sendEmptyMessage(MSG_LOAD_ICONS);
+ } else {
+ sendEmptyMessage(MSG_LOAD_SIZES);
+ }
+ } break;
+ case MSG_LOAD_SIZES: {
+ synchronized (mEntriesMap) {
+ if (mCurComputingSizePkg != null) {
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ for (int i=0; i<mAppEntries.size(); i++) {
+ AppEntry entry = mAppEntries.get(i);
+ if (entry.size == SIZE_UNKNOWN || entry.sizeStale) {
+ if (entry.sizeLoadStart == 0 ||
+ (entry.sizeLoadStart < (now-20*1000))) {
+ if (!mRunning) {
+ mRunning = true;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+ mMainHandler.sendMessage(m);
+ }
+ entry.sizeLoadStart = now;
+ mCurComputingSizePkg = entry.info.packageName;
+ mPm.getPackageSizeInfo(mCurComputingSizePkg, mStatsObserver);
+ }
+ return;
+ }
+ }
+ if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
+ mRunning = false;
+ Message m = mMainHandler.obtainMessage(
+ MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
+ mMainHandler.sendMessage(m);
+ }
+ }
+ } break;
+ }
+ }
+
+ }
+}
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 03ae68a..c2d8774 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -17,32 +17,15 @@
package com.android.settings.applications;
import com.android.settings.R;
+import com.android.settings.applications.ApplicationsState.AppEntry;
-import android.app.ActivityManager;
-import android.app.Dialog;
-import android.app.ProgressDialog;
import android.app.TabActivity;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageStatsObserver;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageStats;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
import android.provider.Settings;
-import android.text.format.Formatter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -50,6 +33,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Filter;
@@ -60,78 +44,24 @@ import android.widget.TabHost;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.text.Collator;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.concurrent.CountDownLatch;
/**
* 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.
- *
- * Initially a compute in progress message is displayed while the application retrieves
- * the list of application information from the PackageManager. The size information
- * for each package is refreshed to the screen. The resource (app description and
- * icon) information for each package is not available yet, so some default values for size
- * icon and descriptions are used initially. Later the resource information for each
- * application is retrieved and dynamically updated on the screen.
- *
- * A Broadcast receiver registers for package additions or deletions when the activity is
- * in focus. If the user installs or deletes packages when the activity has focus, the receiver
- * gets notified and proceeds to add/delete these packages from the list on the screen.
- * This is an unlikely scenario but could happen. The entire list gets created every time
- * the activity's onStart gets invoked. This is to avoid having the receiver for the entire
- * life cycle of the application.
- *
- * The applications can be sorted either alphabetically or
- * based on size (descending). If this activity gets launched under low memory
- * situations (a low memory notification dispatches intent
- * ACTION_MANAGE_PACKAGE_STORAGE) the list is sorted per size.
- *
- * If the user selects an application, extended info (like size, uninstall/clear data options,
- * permissions info etc.,) is displayed via the InstalledAppDetails activity.
*/
public class ManageApplications extends TabActivity implements
OnItemClickListener, DialogInterface.OnCancelListener,
- TabHost.TabContentFactory,
- TabHost.OnTabChangeListener {
- // TAG for this activity
- private static final String TAG = "ManageApplications";
- private static final String PREFS_NAME = "ManageAppsInfo.prefs";
- private static final String PREF_DISABLE_CACHE = "disableCache";
-
- // Log information boolean
- private boolean localLOGV = false;
- private static final boolean DEBUG_SIZE = false;
- private static final boolean DEBUG_TIME = false;
+ TabHost.TabContentFactory, TabHost.OnTabChangeListener {
+ static final String TAG = "ManageApplications";
+ static final boolean DEBUG = false;
// attributes used as keys when passing values to InstalledAppDetails activity
public static final String APP_CHG = "chg";
- // attribute name used in receiver for tagging names of added/deleted packages
- private static final String ATTR_PKG_NAME="p";
- private static final String ATTR_PKGS="ps";
- private static final String ATTR_STATS="ss";
- private static final String ATTR_SIZE_STRS="fs";
-
- private static final String ATTR_GET_SIZE_STATUS="passed";
- private static final String ATTR_PKG_STATS="s";
- private static final String ATTR_PKG_SIZE_STR="f";
-
// constant value that can be used to check return code from sub activity.
private static final int INSTALLED_APP_DETAILS = 1;
@@ -150,83 +80,22 @@ public class ManageApplications extends TabActivity implements
// Filter value
private int mFilterApps = FILTER_APPS_THIRD_PARTY;
- // Custom Adapter used for managing items in the list
- private AppInfoAdapter mAppInfoAdapter;
-
- // messages posted to the handler
- private static final int HANDLER_MESSAGE_BASE = 0;
- private static final int INIT_PKG_INFO = HANDLER_MESSAGE_BASE+1;
- private static final int COMPUTE_BULK_SIZE = HANDLER_MESSAGE_BASE+2;
- private static final int REMOVE_PKG = HANDLER_MESSAGE_BASE+3;
- private static final int REORDER_LIST = HANDLER_MESSAGE_BASE+4;
- private static final int ADD_PKG_START = HANDLER_MESSAGE_BASE+5;
- private static final int ADD_PKG_DONE = HANDLER_MESSAGE_BASE+6;
- private static final int REFRESH_LABELS = HANDLER_MESSAGE_BASE+7;
- private static final int REFRESH_DONE = HANDLER_MESSAGE_BASE+8;
- private static final int NEXT_LOAD_STEP = HANDLER_MESSAGE_BASE+9;
- private static final int COMPUTE_END = HANDLER_MESSAGE_BASE+10;
- private static final int REFRESH_ICONS = HANDLER_MESSAGE_BASE+11;
-
- // observer object used for computing pkg sizes
- private PkgSizeObserver mObserver;
- // local handle to PackageManager
- private PackageManager mPm;
- // Broadcast Receiver object that receives notifications for added/deleted
- // packages
- private PackageIntentReceiver mReceiver;
- // atomic variable used to track if computing pkg sizes is in progress. should be volatile?
-
- private boolean mComputeSizesFinished = false;
- // default icon thats used when displaying applications initially before resource info is
- // retrieved
- private static Drawable mDefaultAppIcon;
-
- // temporary dialog displayed while the application info loads
- private static final int DLG_BASE = 0;
- private static final int DLG_LOADING = DLG_BASE + 1;
+ private ApplicationsState mApplicationsState;
+ private ApplicationsAdapter mApplicationsAdapter;
// Size resource used for packages whose size computation failed for some reason
private CharSequence mInvalidSizeStr;
private CharSequence mComputingSizeStr;
- // map used to store list of added and removed packages. Immutable Boolean
- // variables indicate if a package has been added or removed. If a package is
- // added or deleted multiple times a single entry with the latest operation will
- // be recorded in the map.
- private Map<String, Boolean> mAddRemoveMap;
-
// layout inflater object used to inflate views
private LayoutInflater mInflater;
- // invalid size value used initially and also when size retrieval through PackageManager
- // fails for whatever reason
- private static final int SIZE_INVALID = -1;
-
- // debug boolean variable to test delays from PackageManager API's
- private boolean DEBUG_PKG_DELAY = false;
-
- // Thread to load resources
- ResourceLoaderThread mResourceThread;
- private TaskRunner mSizeComputor;
-
private String mCurrentPkgName;
- // Cache application attributes
- private AppInfoCache mCache = new AppInfoCache();
-
- // Boolean variables indicating state
- private boolean mLoadLabelsFinished = false;
- private boolean mSizesFirst = false;
// ListView used to display list
private ListView mListView;
// Custom view used to display running processes
private RunningProcessesView mRunningProcessesView;
- // State variables used to figure out menu options and also
- // initiate the first computation and loading of resources
- private boolean mJustCreated = true;
- private boolean mFirst = false;
- private long mLoadTimeStart;
- private boolean mSetListViewLater = true;
// These are for keeping track of activity and tab switch state.
private int mCurView;
@@ -236,832 +105,205 @@ public class ManageApplications extends TabActivity implements
private boolean mActivityResumed;
private Object mNonConfigInstance;
- /*
- * Handler class to handle messages for various operations.
- * Most of the operations that effect Application related data
- * are posted as messages to the handler to avoid synchronization
- * when accessing these structures.
- *
- * When the size retrieval gets kicked off for the first time, a COMPUTE_PKG_SIZE_START
- * message is posted to the handler which invokes the getSizeInfo for the pkg at index 0.
- *
- * When the PackageManager's asynchronous call back through
- * PkgSizeObserver.onGetStatsCompleted gets invoked, the application resources like
- * label, description, icon etc., are loaded in the same thread and these values are
- * set on the observer. The observer then posts a COMPUTE_PKG_SIZE_DONE message
- * to the handler. This information is updated on the AppInfoAdapter associated with
- * the list view of this activity and size info retrieval is initiated for the next package as
- * indicated by mComputeIndex.
- *
- * When a package gets added while the activity has focus, the PkgSizeObserver posts
- * ADD_PKG_START message to the handler. If the computation is not in progress, the size
- * is retrieved for the newly added package through the observer object and the newly
- * installed app info is updated on the screen. If the computation is still in progress
- * the package is added to an internal structure and action deferred till the computation
- * is done for all the packages.
- *
- * When a package gets deleted, REMOVE_PKG is posted to the handler
- * if computation is not in progress (as indicated by
- * mDoneIniting), the package is deleted from the displayed list of apps. If computation is
- * still in progress the package is added to an internal structure and action deferred till
- * the computation is done for all packages.
- *
- * When the sizes of all packages is computed, the newly
- * added or removed packages are processed in order.
- * If the user changes the order in which these applications are viewed by hitting the
- * menu key, REORDER_LIST message is posted to the handler. this sorts the list
- * of items based on the sort order.
- */
- private Handler mHandler = new Handler() {
- public void handleMessage(Message msg) {
- boolean status;
- long size;
- String formattedSize;
- Bundle data;
- String pkgName = null;
- data = msg.getData();
- if(data != null) {
- pkgName = data.getString(ATTR_PKG_NAME);
- }
- switch (msg.what) {
- case INIT_PKG_INFO:
- if(localLOGV) Log.i(TAG, "Message INIT_PKG_INFO, justCreated = " + mJustCreated);
- List<ApplicationInfo> newList = null;
- if (!mJustCreated) {
- if (localLOGV) Log.i(TAG, "List already created");
- // Add or delete newly created packages by comparing lists
- newList = getInstalledApps(FILTER_APPS_ALL);
- updateAppList(newList);
- }
- // Retrieve the package list and init some structures
- initAppList(newList, mFilterApps);
- mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
- break;
- case COMPUTE_BULK_SIZE:
- if(localLOGV) Log.i(TAG, "Message COMPUTE_BULK_PKG_SIZE");
- String[] pkgs = data.getStringArray(ATTR_PKGS);
- long[] sizes = data.getLongArray(ATTR_STATS);
- String[] formatted = data.getStringArray(ATTR_SIZE_STRS);
- if(pkgs == null || sizes == null || formatted == null) {
- Log.w(TAG, "Ignoring message");
- break;
- }
- mAppInfoAdapter.bulkUpdateSizes(pkgs, sizes, formatted);
- break;
- case COMPUTE_END:
- mComputeSizesFinished = true;
- mFirst = true;
- mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
- break;
- case REMOVE_PKG:
- if(localLOGV) Log.i(TAG, "Message REMOVE_PKG");
- if(pkgName == null) {
- Log.w(TAG, "Ignoring message:REMOVE_PKG for null pkgName");
- break;
- }
- if (!mComputeSizesFinished) {
- Boolean currB = mAddRemoveMap.get(pkgName);
- if (currB == null || (currB.equals(Boolean.TRUE))) {
- mAddRemoveMap.put(pkgName, Boolean.FALSE);
- }
- break;
- }
- List<String> pkgList = new ArrayList<String>();
- pkgList.add(pkgName);
- mAppInfoAdapter.removeFromList(pkgList);
- break;
- case REORDER_LIST:
- if(localLOGV) Log.i(TAG, "Message REORDER_LIST");
- int menuOption = msg.arg1;
- if((menuOption == SORT_ORDER_ALPHA) ||
- (menuOption == SORT_ORDER_SIZE)) {
- // Option to sort list
- if (menuOption != mSortOrder) {
- mSortOrder = menuOption;
- if (localLOGV) Log.i(TAG, "Changing sort order to "+mSortOrder);
- mAppInfoAdapter.sortList(mSortOrder);
- }
- } else if(menuOption != mFilterApps) {
- // Option to filter list
- mFilterApps = menuOption;
- boolean ret = mAppInfoAdapter.resetAppList(mFilterApps);
- if(!ret) {
- // Reset cache
- mFilterApps = FILTER_APPS_ALL;
- mHandler.sendEmptyMessage(INIT_PKG_INFO);
- sendMessageToHandler(REORDER_LIST, menuOption);
- }
- }
- break;
- case ADD_PKG_START:
- if(localLOGV) Log.i(TAG, "Message ADD_PKG_START");
- if(pkgName == null) {
- Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
- break;
- }
- if (!mComputeSizesFinished || !mLoadLabelsFinished) {
- Boolean currB = mAddRemoveMap.get(pkgName);
- if (currB == null || (currB.equals(Boolean.FALSE))) {
- mAddRemoveMap.put(pkgName, Boolean.TRUE);
- }
- break;
- }
- mObserver.invokeGetSizeInfo(pkgName);
- break;
- case ADD_PKG_DONE:
- if(localLOGV) Log.i(TAG, "Message ADD_PKG_DONE");
- if(pkgName == null) {
- Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
- break;
- }
- status = data.getBoolean(ATTR_GET_SIZE_STATUS);
- if (status) {
- size = data.getLong(ATTR_PKG_STATS);
- formattedSize = data.getString(ATTR_PKG_SIZE_STR);
- if (!mAppInfoAdapter.isInstalled(pkgName)) {
- mAppInfoAdapter.addToList(pkgName, size, formattedSize);
- } else {
- mAppInfoAdapter.updatePackage(pkgName, size, formattedSize);
- }
- }
- break;
- case REFRESH_LABELS:
- Map<String, CharSequence> labelMap = (Map<String, CharSequence>) msg.obj;
- if (labelMap != null) {
- mAppInfoAdapter.bulkUpdateLabels(labelMap);
- }
- break;
- case REFRESH_ICONS:
- Map<String, Drawable> iconMap = (Map<String, Drawable>) msg.obj;
- if (iconMap != null) {
- mAppInfoAdapter.bulkUpdateIcons(iconMap);
- }
- break;
- case REFRESH_DONE:
- mLoadLabelsFinished = true;
- mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
- break;
- case NEXT_LOAD_STEP:
- if (!mCache.isEmpty() && mSetListViewLater) {
- if (localLOGV) Log.i(TAG, "Using cache to populate list view");
- initListView();
- mSetListViewLater = false;
- mFirst = true;
- }
- if (mComputeSizesFinished && mLoadLabelsFinished) {
- doneLoadingData();
- // Check for added/removed packages
- Set<String> keys = mAddRemoveMap.keySet();
- for (String key : keys) {
- if (mAddRemoveMap.get(key) == Boolean.TRUE) {
- // Add the package
- updatePackageList(Intent.ACTION_PACKAGE_ADDED, key);
- } else {
- // Remove the package
- updatePackageList(Intent.ACTION_PACKAGE_REMOVED, key);
- }
- }
- mAddRemoveMap.clear();
- } else if (!mComputeSizesFinished && !mLoadLabelsFinished) {
- // Either load the package labels or initiate get size info
- if (mSizesFirst) {
- initComputeSizes();
- } else {
- initResourceThread();
- }
- } else {
- if (mSetListViewLater) {
- if (localLOGV) Log.i(TAG, "Initing list view for very first time");
- initListView();
- mSetListViewLater = false;
- }
- if (!mComputeSizesFinished) {
- initComputeSizes();
- } else if (!mLoadLabelsFinished) {
- initResourceThread();
- }
- }
- break;
- default:
- break;
- }
- }
- };
-
- private void initListView() {
- // Create list view from the adapter here. Wait till the sort order
- // of list is defined. its either by label or by size. So atleast one of the
- // first steps should have been completed before the list gets filled.
- mAppInfoAdapter.sortBaseList(mSortOrder);
- if (mJustCreated) {
- // Set the adapter here.
- mJustCreated = false;
- mListView.setAdapter(mAppInfoAdapter);
- }
- }
-
- class SizeObserver extends IPackageStatsObserver.Stub {
- private CountDownLatch mCount;
- PackageStats stats;
- boolean succeeded;
-
- public void invokeGetSize(String packageName, CountDownLatch count) {
- mCount = count;
- mPm.getPackageSizeInfo(packageName, this);
- }
-
- public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) {
- succeeded = pSucceeded;
- stats = pStats;
- mCount.countDown();
- }
- }
-
- class TaskRunner extends Thread {
- private List<ApplicationInfo> mPkgList;
- private SizeObserver mSizeObserver;
- private static final int END_MSG = COMPUTE_END;
- private static final int SEND_PKG_SIZES = COMPUTE_BULK_SIZE;
- volatile boolean abort = false;
- static final int MSG_PKG_SIZE = 8;
-
- TaskRunner(List<ApplicationInfo> appList) {
- mPkgList = appList;
- mSizeObserver = new SizeObserver();
- start();
- }
+ // View Holder used when displaying views
+ static class AppViewHolder {
+ ApplicationsState.AppEntry entry;
+ TextView appName;
+ ImageView appIcon;
+ TextView appSize;
+ TextView disabled;
- public void setAbort() {
- abort = true;
- }
-
- public void run() {
- long startTime;
- if (DEBUG_SIZE || DEBUG_TIME) {
- startTime = SystemClock.elapsedRealtime();
+ void updateSizeText(ManageApplications ma) {
+ if (DEBUG) Log.i(TAG, "updateSizeText of " + entry.label + " " + entry
+ + ": " + entry.sizeStr);
+ if (entry.sizeStr != null) {
+ appSize.setText(entry.sizeStr);
+ } else if (entry.size == ApplicationsState.SIZE_INVALID) {
+ appSize.setText(ma.mInvalidSizeStr);
}
- int size = mPkgList.size();
- int numMsgs = size / MSG_PKG_SIZE;
- if (size > (numMsgs * MSG_PKG_SIZE)) {
- numMsgs++;
- }
- int endi = 0;
- for (int j = 0; j < size; j += MSG_PKG_SIZE) {
- long sizes[];
- String formatted[];
- String packages[];
- endi += MSG_PKG_SIZE;
- if (endi > size) {
- endi = size;
- }
- sizes = new long[endi-j];
- formatted = new String[endi-j];
- packages = new String[endi-j];
- for (int i = j; i < endi; i++) {
- if (abort) {
- // Exit if abort has been set.
- break;
- }
- CountDownLatch count = new CountDownLatch(1);
- String packageName = mPkgList.get(i).packageName;
- mSizeObserver.invokeGetSize(packageName, count);
- try {
- count.await();
- } catch (InterruptedException e) {
- Log.i(TAG, "Failed computing size for pkg : "+packageName);
- }
- // Process the package statistics
- PackageStats pStats = mSizeObserver.stats;
- boolean succeeded = mSizeObserver.succeeded;
- long total;
- if(succeeded && pStats != null) {
- total = getTotalSize(pStats);
- } else {
- total = SIZE_INVALID;
- }
- sizes[i-j] = total;
- formatted[i-j] = getSizeStr(total).toString();
- packages[i-j] = packageName;
- }
- // Post update message
- Bundle data = new Bundle();
- data.putStringArray(ATTR_PKGS, packages);
- data.putLongArray(ATTR_STATS, sizes);
- data.putStringArray(ATTR_SIZE_STRS, formatted);
- Message msg = mHandler.obtainMessage(SEND_PKG_SIZES, data);
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
- if (DEBUG_SIZE || DEBUG_TIME) Log.i(TAG, "Took "+
- (SystemClock.elapsedRealtime() - startTime)+
- " ms to compute sizes of all packages ");
- mHandler.sendEmptyMessage(END_MSG);
}
}
/*
- * This method compares the current cache against a new list of
- * installed applications and tries to update the list with add or remove
- * messages.
+ * Custom adapter implementation for the ListView
+ * This adapter maintains a map for each displayed application and its properties
+ * An index value on each AppInfo object indicates the correct position or index
+ * in the list. If the list gets updated dynamically when the user is viewing the list of
+ * applications, we need to return the correct index of position. This is done by mapping
+ * the getId methods via the package name into the internal maps and indices.
+ * The order of applications in the list is mirrored in mAppLocalList
*/
- private boolean updateAppList(List<ApplicationInfo> newList) {
- if ((newList == null) || mCache.isEmpty()) {
- return false;
- }
- Set<String> existingList = new HashSet<String>();
- boolean ret = false;
- // Loop over new list and find out common elements between old and new lists
- int N = newList.size();
- for (int i = (N-1); i >= 0; i--) {
- ApplicationInfo info = newList.get(i);
- String pkgName = info.packageName;
- AppInfo aInfo = mCache.getEntry(pkgName);
- if (aInfo != null) {
- existingList.add(pkgName);
- } else {
- // New package. update info by refreshing
- if (localLOGV) Log.i(TAG, "New pkg :"+pkgName+" installed when paused");
- updatePackageList(Intent.ACTION_PACKAGE_ADDED, pkgName);
- // Remove from current list so that the newly added package can
- // be handled later
- newList.remove(i);
- ret = true;
+ class ApplicationsAdapter extends BaseAdapter implements Filterable,
+ ApplicationsState.Callbacks, AbsListView.RecyclerListener {
+ private final ApplicationsState mState;
+ private final ArrayList<View> mActive = new ArrayList<View>();
+ private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
+ private ArrayList<ApplicationsState.AppEntry> mEntries;
+ private boolean mResumed;
+ private int mLastFilterMode=-1, mLastSortMode=-1;
+ CharSequence mCurFilterPrefix;
+
+ private Filter mFilter = new Filter() {
+ @Override
+ protected FilterResults performFiltering(CharSequence constraint) {
+ ArrayList<ApplicationsState.AppEntry> entries
+ = applyPrefixFilter(constraint, mBaseEntries);
+ FilterResults fr = new FilterResults();
+ fr.values = entries;
+ fr.count = entries.size();
+ return fr;
}
- }
- // Loop over old list and figure out stale entries
- List<String> deletedList = null;
- Set<String> staleList = mCache.getPkgList();
- for (String pkgName : staleList) {
- if (!existingList.contains(pkgName)) {
- if (localLOGV) Log.i(TAG, "Pkg :"+pkgName+" deleted when paused");
- if (deletedList == null) {
- deletedList = new ArrayList<String>();
- deletedList.add(pkgName);
- }
- ret = true;
- }
- }
- // Delete right away
- if (deletedList != null) {
- if (localLOGV) Log.i(TAG, "Deleting right away");
- mAppInfoAdapter.removeFromList(deletedList);
- }
- return ret;
- }
-
- private void doneLoadingData() {
- setProgressBarIndeterminateVisibility(false);
- }
-
- List<ApplicationInfo> getInstalledApps(int filterOption) {
- List<ApplicationInfo> installedAppList = mPm.getInstalledApplications(
- PackageManager.GET_UNINSTALLED_PACKAGES |
- PackageManager.GET_DISABLED_COMPONENTS);
- if (installedAppList == null) {
- return new ArrayList<ApplicationInfo> ();
- }
- if (filterOption == FILTER_APPS_SDCARD) {
- List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
- for (ApplicationInfo appInfo : installedAppList) {
- if ((appInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- // App on sdcard
- appList.add(appInfo);
- }
- }
- return appList;
- } else if (filterOption == FILTER_APPS_THIRD_PARTY) {
- List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
- for (ApplicationInfo appInfo : installedAppList) {
- boolean flag = false;
- if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
- // Updated system app
- flag = true;
- } else if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- // Non-system app
- flag = true;
- }
- if (flag) {
- appList.add(appInfo);
- }
+ @Override
+ protected void publishResults(CharSequence constraint, FilterResults results) {
+ mCurFilterPrefix = constraint;
+ mEntries = (ArrayList<ApplicationsState.AppEntry>)results.values;
+ notifyDataSetChanged();
}
- return appList;
- } else {
- return installedAppList;
- }
- }
+ };
- private static boolean matchFilter(boolean filter, Map<String, String> filterMap, String pkg) {
- boolean add = true;
- if (filter) {
- if (filterMap == null || !filterMap.containsKey(pkg)) {
- add = false;
- }
+ public ApplicationsAdapter(ApplicationsState state) {
+ mState = state;
}
- return add;
- }
-
- /*
- * Utility method used to figure out list of apps based on filterOption
- * If the framework supports an additional flag to indicate running apps
- * we can get away with some code here.
- */
- List<ApplicationInfo> getFilteredApps(List<ApplicationInfo> pAppList, int filterOption, boolean filter,
- Map<String, String> filterMap) {
- List<ApplicationInfo> retList = new ArrayList<ApplicationInfo>();
- if(pAppList == null) {
- return retList;
- }
- if (filterOption == FILTER_APPS_SDCARD) {
- for (ApplicationInfo appInfo : pAppList) {
- boolean flag = false;
- if ((appInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- // App on sdcard
- flag = true;
- }
- if (flag) {
- if (matchFilter(filter, filterMap, appInfo.packageName)) {
- retList.add(appInfo);
- }
- }
- }
- return retList;
- } else if (filterOption == FILTER_APPS_THIRD_PARTY) {
- for (ApplicationInfo appInfo : pAppList) {
- boolean flag = false;
- if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
- // Updated system app
- flag = true;
- } else if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- // Non-system app
- flag = true;
- }
- if (flag) {
- if (matchFilter(filter, filterMap, appInfo.packageName)) {
- retList.add(appInfo);
- }
- }
- }
- return retList;
- } else {
- for (ApplicationInfo appInfo : pAppList) {
- if (matchFilter(filter, filterMap, appInfo.packageName)) {
- retList.add(appInfo);
- }
+
+ public void resume(int filter, int sort) {
+ if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed);
+ if (!mResumed) {
+ mResumed = true;
+ mState.resume(this);
+ mLastFilterMode = filter;
+ mLastSortMode = sort;
+ rebuild();
+ } else {
+ rebuild(filter, sort);
}
- return retList;
}
- }
- // Some initialization code used when kicking off the size computation
- private void initAppList(List<ApplicationInfo> appList, int filterOption) {
- setProgressBarIndeterminateVisibility(true);
- mComputeSizesFinished = false;
- mLoadLabelsFinished = false;
- // Initialize lists
- mAddRemoveMap = new TreeMap<String, Boolean>();
- mAppInfoAdapter.initMapFromList(appList, filterOption);
- }
-
- // Utility method to start a thread to read application labels and icons
- private void initResourceThread() {
- if ((mResourceThread != null) && mResourceThread.isAlive()) {
- mResourceThread.setAbort();
- }
- mResourceThread = new ResourceLoaderThread();
- List<ApplicationInfo> appList = mAppInfoAdapter.getBaseAppList();
- if ((appList != null) && (appList.size()) > 0) {
- mResourceThread.loadAllResources(appList);
+ public void pause() {
+ if (mResumed) {
+ mResumed = false;
+ mState.pause();
+ }
}
- }
- private void initComputeSizes() {
- // Initiate compute package sizes
- if (localLOGV) Log.i(TAG, "Initiating compute sizes for first time");
- if ((mSizeComputor != null) && (mSizeComputor.isAlive())) {
- mSizeComputor.setAbort();
- }
- List<ApplicationInfo> appList = mAppInfoAdapter.getBaseAppList();
- if ((appList != null) && (appList.size()) > 0) {
- mSizeComputor = new TaskRunner(appList);
- } else {
- mComputeSizesFinished = true;
- }
- }
-
- // internal structure used to track added and deleted packages when
- // the activity has focus
- static class AddRemoveInfo {
- String pkgName;
- boolean add;
- public AddRemoveInfo(String pPkgName, boolean pAdd) {
- pkgName = pPkgName;
- add = pAdd;
+ public void rebuild(int filter, int sort) {
+ if (filter == mLastFilterMode && sort == mLastSortMode) {
+ return;
+ }
+ mLastFilterMode = filter;
+ mLastSortMode = sort;
+ rebuild();
}
- }
-
- class ResourceLoaderThread extends Thread {
- List<ApplicationInfo> mAppList;
- volatile boolean abort = false;
- static final int MSG_PKG_SIZE = 8;
- public void setAbort() {
- abort = true;
- }
- void loadAllResources(List<ApplicationInfo> appList) {
- mAppList = appList;
- start();
+ public void rebuild() {
+ if (DEBUG) Log.i(TAG, "Rebuilding app list...");
+ ApplicationsState.AppFilter filterObj;
+ Comparator<AppEntry> comparatorObj;
+ switch (mLastFilterMode) {
+ case FILTER_APPS_THIRD_PARTY:
+ filterObj = ApplicationsState.THIRD_PARTY_FILTER;
+ break;
+ case FILTER_APPS_SDCARD:
+ filterObj = ApplicationsState.ON_SD_CARD_FILTER;
+ break;
+ default:
+ filterObj = null;
+ break;
+ }
+ switch (mLastSortMode) {
+ case SORT_ORDER_SIZE:
+ comparatorObj = ApplicationsState.SIZE_COMPARATOR;
+ break;
+ default:
+ comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
+ break;
+ }
+ mBaseEntries = mState.rebuild(filterObj, comparatorObj);
+ mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
+ notifyDataSetChanged();
}
- public void run() {
- long start;
- if (DEBUG_TIME) {
- start = SystemClock.elapsedRealtime();
- }
- int imax;
- if(mAppList == null || (imax = mAppList.size()) <= 0) {
- Log.w(TAG, "Empty or null application list");
+ ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
+ ArrayList<ApplicationsState.AppEntry> origEntries) {
+ if (prefix == null || prefix.length() == 0) {
+ return origEntries;
} else {
- int size = mAppList.size();
- int numMsgs = size / MSG_PKG_SIZE;
- if (size > (numMsgs * MSG_PKG_SIZE)) {
- numMsgs++;
- }
- int endi = 0;
- for (int j = 0; j < size; j += MSG_PKG_SIZE) {
- Map<String, CharSequence> map = new HashMap<String, CharSequence>();
- endi += MSG_PKG_SIZE;
- if (endi > size) {
- endi = size;
+ String prefixStr = ApplicationsState.normalize(prefix.toString());
+ final String spacePrefixStr = " " + prefixStr;
+ ArrayList<ApplicationsState.AppEntry> newEntries
+ = new ArrayList<ApplicationsState.AppEntry>();
+ for (int i=0; i<origEntries.size(); i++) {
+ ApplicationsState.AppEntry entry = origEntries.get(i);
+ String nlabel = entry.getNormalizedLabel();
+ if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
+ newEntries.add(entry);
}
- for (int i = j; i < endi; i++) {
- if (abort) {
- // Exit if abort has been set.
- break;
- }
- ApplicationInfo appInfo = mAppList.get(i);
- map.put(appInfo.packageName, appInfo.loadLabel(mPm));
- }
- // Post update message
- Message msg = mHandler.obtainMessage(REFRESH_LABELS);
- msg.obj = map;
- mHandler.sendMessage(msg);
- }
- Message doneMsg = mHandler.obtainMessage(REFRESH_DONE);
- mHandler.sendMessage(doneMsg);
- if (DEBUG_TIME) Log.i(TAG, "Took "+(SystemClock.elapsedRealtime()-start)+
- " ms to load app labels");
- long startIcons;
- if (DEBUG_TIME) {
- startIcons = SystemClock.elapsedRealtime();
}
- Map<String, Drawable> map = new HashMap<String, Drawable>();
- for (int i = (imax-1); i >= 0; i--) {
- if (abort) {
- return;
- }
- ApplicationInfo appInfo = mAppList.get(i);
- map.put(appInfo.packageName, appInfo.loadIcon(mPm));
- }
- Message msg = mHandler.obtainMessage(REFRESH_ICONS);
- msg.obj = map;
- mHandler.sendMessage(msg);
- if (DEBUG_TIME) Log.i(TAG, "Took "+(SystemClock.elapsedRealtime()-startIcons)+" ms to load app icons");
+ return newEntries;
}
- if (DEBUG_TIME) Log.i(TAG, "Took "+(SystemClock.elapsedRealtime()-start)+" ms to load app resources");
}
- }
-
- /* Internal class representing an application or packages displayable attributes
- *
- */
- static private class AppInfo {
- public String pkgName;
- int index;
- public CharSequence appName;
- public Drawable appIcon;
- public CharSequence appSize;
- long size;
- public void refreshIcon(Drawable icon) {
- if (icon == null) {
- return;
- }
- appIcon = icon;
- }
- public void refreshLabel(CharSequence label) {
- if (label == null) {
- return;
- }
- appName = label;
+ @Override
+ public void onRunningStateChanged(boolean running) {
+ setProgressBarIndeterminateVisibility(running);
}
- public AppInfo(String pName, int pIndex, CharSequence aName,
- long pSize,
- CharSequence pSizeStr) {
- this(pName, pIndex, aName, mDefaultAppIcon, pSize, pSizeStr);
- }
-
- public AppInfo(String pName, int pIndex, CharSequence aName, Drawable aIcon,
- long pSize,
- CharSequence pSizeStr) {
- index = pIndex;
- pkgName = pName;
- appName = aName;
- appIcon = aIcon;
- size = pSize;
- appSize = pSizeStr;
+ @Override
+ public void onPackageListChanged() {
+ rebuild();
}
-
- public boolean setSize(long newSize, String formattedSize) {
- if (size != newSize) {
- size = newSize;
- appSize = formattedSize;
- return true;
- }
- return false;
- }
- }
-
- private long getTotalSize(PackageStats ps) {
- if (ps != null) {
- return ps.cacheSize+ps.codeSize+ps.dataSize;
- }
- return SIZE_INVALID;
- }
- private CharSequence getSizeStr(long size) {
- CharSequence appSize = null;
- if (size == SIZE_INVALID) {
- return mInvalidSizeStr;
+ @Override
+ public void onPackageIconChanged() {
+ // We ensure icons are loaded when their item is displayed, so
+ // don't care about icons loaded in the background.
}
- appSize = Formatter.formatFileSize(ManageApplications.this, size);
- return appSize;
- }
- // View Holder used when displaying views
- static class AppViewHolder {
- TextView appName;
- ImageView appIcon;
- TextView appSize;
- TextView disabled;
- }
-
- /*
- * Custom adapter implementation for the ListView
- * This adapter maintains a map for each displayed application and its properties
- * An index value on each AppInfo object indicates the correct position or index
- * in the list. If the list gets updated dynamically when the user is viewing the list of
- * applications, we need to return the correct index of position. This is done by mapping
- * the getId methods via the package name into the internal maps and indices.
- * The order of applications in the list is mirrored in mAppLocalList
- */
- class AppInfoAdapter extends BaseAdapter implements Filterable {
- private List<ApplicationInfo> mAppList;
- private List<ApplicationInfo> mAppLocalList;
- private Map<String, String> mFilterMap = new HashMap<String, String>();
- AlphaComparator mAlphaComparator = new AlphaComparator();
- SizeComparator mSizeComparator = new SizeComparator();
- private Filter mAppFilter = new AppFilter();
- final private Object mFilterLock = new Object();
- private Map<String, String> mCurrentFilterMap = null;
-
- private void generateFilterListLocked(List<ApplicationInfo> list) {
- mAppLocalList = new ArrayList<ApplicationInfo>(list);
- synchronized(mFilterLock) {
- for (ApplicationInfo info : mAppLocalList) {
- String label = info.packageName;
- AppInfo aInfo = mCache.getEntry(info.packageName);
- if ((aInfo != null) && (aInfo.appName != null)) {
- label = aInfo.appName.toString();
+ @Override
+ public void onPackageSizeChanged(String packageName) {
+ for (int i=0; i<mActive.size(); i++) {
+ AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag();
+ if (holder.entry.info.packageName.equals(packageName)) {
+ synchronized (holder.entry) {
+ holder.updateSizeText(ManageApplications.this);
}
- mFilterMap.put(info.packageName, label.toLowerCase());
- }
- }
- }
-
- private void addFilterListLocked(int newIdx, ApplicationInfo info, CharSequence pLabel) {
- mAppLocalList.add(newIdx, info);
- synchronized (mFilterLock) {
- String label = info.packageName;
- if (pLabel != null) {
- label = pLabel.toString();
- }
- mFilterMap.put(info.packageName, label.toLowerCase());
- }
- }
-
- private boolean removeFilterListLocked(String removePkg) {
- // Remove from filtered list
- int N = mAppLocalList.size();
- int i;
- for (i = (N-1); i >= 0; i--) {
- ApplicationInfo info = mAppLocalList.get(i);
- if (info.packageName.equalsIgnoreCase(removePkg)) {
- if (localLOGV) Log.i(TAG, "Removing " + removePkg + " from local list");
- mAppLocalList.remove(i);
- synchronized (mFilterLock) {
- mFilterMap.remove(removePkg);
+ if (holder.entry.info.packageName.equals(mCurrentPkgName)
+ && mLastSortMode == SORT_ORDER_SIZE) {
+ // We got the size information for the last app the
+ // user viewed, and are sorting by size... they may
+ // have cleared data, so we immediately want to resort
+ // the list with the new size to reflect it to the user.
+ rebuild();
}
- return true;
+ return;
}
}
- return false;
}
- private void reverseGenerateList() {
- generateFilterListLocked(getFilteredApps(mAppList, mFilterApps, mCurrentFilterMap!= null, mCurrentFilterMap));
- sortListInner(mSortOrder);
- }
-
- // Make sure the cache or map contains entries for all elements
- // in appList for a valid sort.
- public void initMapFromList(List<ApplicationInfo> pAppList, int filterOption) {
- boolean notify = false;
- List<ApplicationInfo> appList = null;
- if (pAppList == null) {
- // Just refresh the list
- appList = mAppList;
- } else {
- mAppList = new ArrayList<ApplicationInfo>(pAppList);
- appList = pAppList;
- notify = true;
- }
- generateFilterListLocked(getFilteredApps(appList, filterOption, mCurrentFilterMap!= null, mCurrentFilterMap));
- // This loop verifies and creates new entries for new packages in list
- int imax = appList.size();
- for (int i = 0; i < imax; i++) {
- ApplicationInfo info = appList.get(i);
- AppInfo aInfo = mCache.getEntry(info.packageName);
- if(aInfo == null){
- aInfo = new AppInfo(info.packageName, i,
- info.packageName, -1, mComputingSizeStr);
- if (localLOGV) Log.i(TAG, "Creating entry pkg:"+info.packageName+" to map");
- mCache.addEntry(aInfo);
- }
- }
- sortListInner(mSortOrder);
- if (notify) {
- notifyDataSetChanged();
+ @Override
+ public void onAllSizesComputed() {
+ if (mLastSortMode == SORT_ORDER_SIZE) {
+ rebuild();
}
}
- public AppInfoAdapter(Context c, List<ApplicationInfo> appList) {
- mAppList = appList;
- }
-
public int getCount() {
- return mAppLocalList.size();
+ return mEntries != null ? mEntries.size() : 0;
}
public Object getItem(int position) {
- return mAppLocalList.get(position);
+ return mEntries.get(position);
}
- public boolean isInstalled(String pkgName) {
- if(pkgName == null) {
- if (localLOGV) Log.w(TAG, "Null pkg name when checking if installed");
- return false;
- }
- for (ApplicationInfo info : mAppList) {
- if (info.packageName.equalsIgnoreCase(pkgName)) {
- return true;
- }
- }
- return false;
- }
-
- public ApplicationInfo getApplicationInfo(int position) {
- int imax = mAppLocalList.size();
- if( (position < 0) || (position >= imax)) {
- Log.w(TAG, "Position out of bounds in List Adapter");
- return null;
- }
- return mAppLocalList.get(position);
+ public ApplicationsState.AppEntry getAppEntry(int position) {
+ return mEntries.get(position);
}
public long getItemId(int position) {
- int imax = mAppLocalList.size();
- if( (position < 0) || (position >= imax)) {
- Log.w(TAG, "Position out of bounds in List Adapter");
- return -1;
- }
- AppInfo aInfo = mCache.getEntry(mAppLocalList.get(position).packageName);
- if (aInfo == null) {
- return -1;
- }
- return aInfo.index;
- }
-
- public List<ApplicationInfo> getBaseAppList() {
- return mAppList;
+ return mEntries.get(position).id;
}
public View getView(int position, View convertView, ViewGroup parent) {
- if (position >= mAppLocalList.size()) {
- Log.w(TAG, "Invalid view position:"+position+", actual size is:"+mAppLocalList.size());
- return null;
- }
// A ViewHolder keeps references to children views to avoid unnecessary calls
// to findViewById() on each row.
AppViewHolder holder;
@@ -1087,503 +329,35 @@ public class ManageApplications extends TabActivity implements
}
// Bind the data efficiently with the holder
- ApplicationInfo appInfo = mAppLocalList.get(position);
- AppInfo mInfo = mCache.getEntry(appInfo.packageName);
- if(mInfo != null) {
- if (mInfo.appName != null) {
- holder.appName.setText(mInfo.appName);
+ ApplicationsState.AppEntry entry = mEntries.get(position);
+ synchronized (entry) {
+ holder.entry = entry;
+ if (entry.label != null) {
+ holder.appName.setText(entry.label);
holder.appName.setTextColor(getResources().getColorStateList(
- appInfo.enabled ? android.R.color.primary_text_dark
+ entry.info.enabled ? android.R.color.primary_text_dark
: android.R.color.secondary_text_dark));
}
- if (mInfo.appIcon != null) {
- holder.appIcon.setImageDrawable(mInfo.appIcon);
- }
- if (mInfo.appSize != null) {
- holder.appSize.setText(mInfo.appSize);
+ mState.ensureIcon(entry);
+ if (entry.icon != null) {
+ holder.appIcon.setImageDrawable(entry.icon);
}
- holder.disabled.setVisibility(appInfo.enabled ? View.GONE : View.VISIBLE);
- } else {
- Log.w(TAG, "No info for package:"+appInfo.packageName+" in property map");
+ holder.updateSizeText(ManageApplications.this);
+ holder.disabled.setVisibility(entry.info.enabled ? View.GONE : View.VISIBLE);
}
+ mActive.remove(convertView);
+ mActive.add(convertView);
return convertView;
}
-
- private void adjustIndex() {
- int imax = mAppLocalList.size();
- for (int i = 0; i < imax; i++) {
- ApplicationInfo info = mAppLocalList.get(i);
- mCache.getEntry(info.packageName).index = i;
- }
- }
-
- public void sortAppList(List<ApplicationInfo> appList, int sortOrder) {
- Collections.sort(appList, getAppComparator(sortOrder));
- }
-
- public void sortBaseList(int sortOrder) {
- if (localLOGV) Log.i(TAG, "Sorting base list based on sortOrder = "+sortOrder);
- sortAppList(mAppList, sortOrder);
- generateFilterListLocked(getFilteredApps(mAppList, mFilterApps, mCurrentFilterMap!= null, mCurrentFilterMap));
- adjustIndex();
- }
-
- private void sortListInner(int sortOrder) {
- sortAppList(mAppLocalList, sortOrder);
- adjustIndex();
- }
-
- public void sortList(int sortOrder) {
- if (localLOGV) Log.i(TAG, "sortOrder = "+sortOrder);
- sortListInner(sortOrder);
- notifyDataSetChanged();
- }
-
- /*
- * Reset the application list associated with this adapter.
- * @param filterOption Sort the list based on this value
- * @param appList the actual application list that is used to reset
- * @return Return a boolean value to indicate inconsistency
- */
- public boolean resetAppList(int filterOption) {
- // Change application list based on filter option
- generateFilterListLocked(getFilteredApps(mAppList, filterOption, mCurrentFilterMap!= null, mCurrentFilterMap));
- // Check for all properties in map before sorting. Populate values from cache
- for(ApplicationInfo applicationInfo : mAppLocalList) {
- AppInfo appInfo = mCache.getEntry(applicationInfo.packageName);
- if(appInfo == null) {
- Log.i(TAG, " Entry does not exist for pkg: " + applicationInfo.packageName);
- }
- }
- if (mAppLocalList.size() > 0) {
- sortList(mSortOrder);
- } else {
- notifyDataSetChanged();
- }
- return true;
- }
-
- private Comparator<ApplicationInfo> getAppComparator(int sortOrder) {
- if (sortOrder == SORT_ORDER_ALPHA) {
- return mAlphaComparator;
- }
- return mSizeComparator;
- }
-
- public void bulkUpdateIcons(Map<String, Drawable> icons) {
- if (icons == null) {
- return;
- }
- Set<String> keys = icons.keySet();
- boolean changed = false;
- for (String key : keys) {
- Drawable ic = icons.get(key);
- if (ic != null) {
- AppInfo aInfo = mCache.getEntry(key);
- if (aInfo != null) {
- aInfo.refreshIcon(ic);
- changed = true;
- }
- }
- }
- if (changed) {
- notifyDataSetChanged();
- }
- }
-
- public void bulkUpdateLabels(Map<String, CharSequence> map) {
- if (map == null) {
- return;
- }
- Set<String> keys = map.keySet();
- boolean changed = false;
- for (String key : keys) {
- CharSequence label = map.get(key);
- AppInfo aInfo = mCache.getEntry(key);
- if (aInfo != null) {
- aInfo.refreshLabel(label);
- changed = true;
- }
- }
- if (changed) {
- notifyDataSetChanged();
- }
- }
-
- private boolean shouldBeInList(int filterOption, ApplicationInfo info) {
- // Match filter here
- if (filterOption == FILTER_APPS_THIRD_PARTY) {
- if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- return true;
- } else if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
- return true;
- }
- } else if (filterOption == FILTER_APPS_SDCARD) {
- if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- return true;
- }
- } else {
- return true;
- }
- return false;
- }
-
- /*
- * Add a package to the current list.
- * The package is only added to the displayed list
- * based on the filter value. The package is always added to the property map.
- * @param pkgName name of package to be added
- * @param ps PackageStats of new package
- */
- public void addToList(String pkgName, long size, String formattedSize) {
- if (pkgName == null) {
- return;
- }
- // Get ApplicationInfo
- ApplicationInfo info = null;
- try {
- info = mPm.getApplicationInfo(pkgName, 0);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Ignoring non-existent package:"+pkgName);
- return;
- }
- if(info == null) {
- // Nothing to do log error message and return
- Log.i(TAG, "Null ApplicationInfo for package:"+pkgName);
- return;
- }
- // Add entry to base list
- mAppList.add(info);
- // Add entry to map. Note that the index gets adjusted later on based on
- // whether the newly added package is part of displayed list
- CharSequence label = info.loadLabel(mPm);
- mCache.addEntry(new AppInfo(pkgName, -1,
- label, info.loadIcon(mPm), size, formattedSize));
- if (addLocalEntry(info, label)) {
- notifyDataSetChanged();
- }
- }
-
- private boolean addLocalEntry(ApplicationInfo info, CharSequence label) {
- String pkgName = info.packageName;
- // Add to list
- if (shouldBeInList(mFilterApps, info)) {
- // Binary search returns a negative index (ie -index) of the position where
- // this might be inserted.
- int newIdx = Collections.binarySearch(mAppLocalList, info,
- getAppComparator(mSortOrder));
- if(newIdx >= 0) {
- if (localLOGV) Log.i(TAG, "Strange. Package:" + pkgName + " is not new");
- return false;
- }
- // New entry
- newIdx = -newIdx-1;
- addFilterListLocked(newIdx, info, label);
- // Adjust index
- adjustIndex();
- return true;
- }
- return false;
- }
-
- public void updatePackage(String pkgName,
- long size, String formattedSize) {
- ApplicationInfo info = null;
- try {
- info = mPm.getApplicationInfo(pkgName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
- } catch (NameNotFoundException e) {
- return;
- }
- AppInfo aInfo = mCache.getEntry(pkgName);
- if (aInfo != null) {
- CharSequence label = info.loadLabel(mPm);
- aInfo.refreshLabel(label);
- aInfo.refreshIcon(info.loadIcon(mPm));
- aInfo.setSize(size, formattedSize);
- // Check if the entry has to be added to the displayed list
- addLocalEntry(info, label);
- // Refresh list since size might have changed
- notifyDataSetChanged();
- }
- }
-
- private void removePkgBase(String pkgName) {
- int imax = mAppList.size();
- for (int i = 0; i < imax; i++) {
- ApplicationInfo app = mAppList.get(i);
- if (app.packageName.equalsIgnoreCase(pkgName)) {
- if (localLOGV) Log.i(TAG, "Removing pkg: "+pkgName+" from base list");
- mAppList.remove(i);
- return;
- }
- }
- }
-
- public void removeFromList(List<String> pkgNames) {
- if(pkgNames == null) {
- return;
- }
- if(pkgNames.size() <= 0) {
- return;
- }
- boolean found = false;
- for (String pkg : pkgNames) {
- // Remove from the base application list
- removePkgBase(pkg);
- // Remove from cache
- if (localLOGV) Log.i(TAG, "Removing " + pkg + " from cache");
- mCache.removeEntry(pkg);
- // Remove from filtered list
- if (removeFilterListLocked(pkg)) {
- found = true;
- }
- }
- // Adjust indices of list entries
- if (found) {
- adjustIndex();
- if (localLOGV) Log.i(TAG, "adjusting index and notifying list view");
- notifyDataSetChanged();
- }
- }
-
- public void bulkUpdateSizes(String pkgs[], long sizes[], String formatted[]) {
- if(pkgs == null || sizes == null || formatted == null) {
- return;
- }
- boolean changed = false;
- for (int i = 0; i < pkgs.length; i++) {
- AppInfo entry = mCache.getEntry(pkgs[i]);
- if (entry == null) {
- if (localLOGV) Log.w(TAG, "Entry for package:"+ pkgs[i] +"doesn't exist in map");
- continue;
- }
- if (entry.setSize(sizes[i], formatted[i])) {
- changed = true;
- }
- }
- if (changed) {
- notifyDataSetChanged();
- }
- }
+ @Override
public Filter getFilter() {
- return mAppFilter;
+ return mFilter;
}
- private class AppFilter extends Filter {
- @Override
- protected FilterResults performFiltering(CharSequence prefix) {
- FilterResults results = new FilterResults();
- if (prefix == null || prefix.length() == 0) {
- synchronized (mFilterLock) {
- results.values = new HashMap<String, String>(mFilterMap);
- results.count = mFilterMap.size();
- }
- } else {
- final String prefixString = prefix.toString().toLowerCase();
- final String spacePrefixString = " " + prefixString;
- Map<String, String> newMap = new HashMap<String, String>();
- synchronized (mFilterLock) {
- Map<String, String> localMap = mFilterMap;
- Set<String> keys = mFilterMap.keySet();
- for (String key : keys) {
- String label = localMap.get(key);
- if (label == null) continue;
- label = label.toLowerCase();
- if (label.startsWith(prefixString)
- || label.indexOf(spacePrefixString) != -1) {
- newMap.put(key, label);
- }
- }
- }
- results.values = newMap;
- results.count = newMap.size();
- }
- return results;
- }
-
- @Override
- protected void publishResults(CharSequence constraint, FilterResults results) {
- mCurrentFilterMap = (Map<String, String>) results.values;
- reverseGenerateList();
- if (results.count > 0) {
- notifyDataSetChanged();
- } else {
- notifyDataSetInvalidated();
- }
- }
- }
- }
-
- /*
- * Utility method to clear messages to Handler
- * We need'nt synchronize on the Handler since posting messages is guaranteed
- * to be thread safe. Even if the other thread that retrieves package sizes
- * posts a message, we do a cursory check of validity on mAppInfoAdapter's applist
- */
- private void clearMessagesInHandler() {
- mHandler.removeMessages(INIT_PKG_INFO);
- mHandler.removeMessages(COMPUTE_BULK_SIZE);
- mHandler.removeMessages(REMOVE_PKG);
- mHandler.removeMessages(REORDER_LIST);
- mHandler.removeMessages(ADD_PKG_START);
- mHandler.removeMessages(ADD_PKG_DONE);
- mHandler.removeMessages(REFRESH_LABELS);
- mHandler.removeMessages(REFRESH_DONE);
- mHandler.removeMessages(NEXT_LOAD_STEP);
- mHandler.removeMessages(COMPUTE_END);
- }
-
- private void sendMessageToHandler(int msgId, int arg1) {
- Message msg = mHandler.obtainMessage(msgId);
- msg.arg1 = arg1;
- mHandler.sendMessage(msg);
- }
-
- private void sendMessageToHandler(int msgId, Bundle data) {
- Message msg = mHandler.obtainMessage(msgId);
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
-
- private void sendMessageToHandler(int msgId) {
- mHandler.sendEmptyMessage(msgId);
- }
-
- /*
- * Stats Observer class used to compute package sizes and retrieve size information
- * PkgSizeOberver is the call back thats used when invoking getPackageSizeInfo on
- * PackageManager. The values in call back onGetStatsCompleted are validated
- * and the specified message is passed to mHandler. The package name
- * and the AppInfo object corresponding to the package name are set on the message
- */
- class PkgSizeObserver extends IPackageStatsObserver.Stub {
- String pkgName;
- public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) {
- if(DEBUG_PKG_DELAY) {
- try {
- Thread.sleep(10*1000);
- } catch (InterruptedException e) {
- }
- }
- Bundle data = new Bundle();
- data.putString(ATTR_PKG_NAME, pkgName);
- data.putBoolean(ATTR_GET_SIZE_STATUS, pSucceeded);
- if(pSucceeded && pStats != null) {
- if (localLOGV) Log.i(TAG, "onGetStatsCompleted::"+pkgName+", ("+
- pStats.cacheSize+","+
- pStats.codeSize+", "+pStats.dataSize);
- long total = getTotalSize(pStats);
- data.putLong(ATTR_PKG_STATS, total);
- CharSequence sizeStr = getSizeStr(total);
- data.putString(ATTR_PKG_SIZE_STR, sizeStr.toString());
- } else {
- Log.w(TAG, "Invalid package stats from PackageManager");
- }
- // Post message to Handler
- Message msg = mHandler.obtainMessage(ADD_PKG_DONE, data);
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
-
- public void invokeGetSizeInfo(String packageName) {
- if (packageName == null) {
- return;
- }
- pkgName = packageName;
- if(localLOGV) Log.i(TAG, "Invoking getPackageSizeInfo for package:"+
- packageName);
- mPm.getPackageSizeInfo(packageName, this);
- }
- }
-
- /**
- * Receives notifications when applications are added/removed.
- */
- private class PackageIntentReceiver extends BroadcastReceiver {
- void registerReceiver() {
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addDataScheme("package");
- ManageApplications.this.registerReceiver(this, filter);
- // Register for events related to sdcard installation.
- IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- ManageApplications.this.registerReceiver(this, sdFilter);
- }
- @Override
- public void onReceive(Context context, Intent intent) {
- String actionStr = intent.getAction();
- if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr) ||
- Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
- Uri data = intent.getData();
- String pkgName = data.getEncodedSchemeSpecificPart();
- updatePackageList(actionStr, pkgName);
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
- Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
- // When applications become available or unavailable (perhaps because
- // the SD card was inserted or ejected) we need to refresh the
- // AppInfo with new label, icon and size information as appropriate
- // given the newfound (un)availability of the application.
- // A simple way to do that is to treat the refresh as a package
- // removal followed by a package addition.
- String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- if (pkgList == null || pkgList.length == 0) {
- // Ignore
- return;
- }
- for (String pkgName : pkgList) {
- updatePackageList(Intent.ACTION_PACKAGE_REMOVED, pkgName);
- updatePackageList(Intent.ACTION_PACKAGE_ADDED, pkgName);
- }
- }
- }
- }
-
- private void updatePackageList(String actionStr, String pkgName) {
- if (Intent.ACTION_PACKAGE_ADDED.equalsIgnoreCase(actionStr)) {
- Bundle data = new Bundle();
- data.putString(ATTR_PKG_NAME, pkgName);
- sendMessageToHandler(ADD_PKG_START, data);
- } else if (Intent.ACTION_PACKAGE_REMOVED.equalsIgnoreCase(actionStr)) {
- Bundle data = new Bundle();
- data.putString(ATTR_PKG_NAME, pkgName);
- sendMessageToHandler(REMOVE_PKG, data);
- }
- }
-
- static final int VIEW_NOTHING = 0;
- static final int VIEW_LIST = 1;
- static final int VIEW_RUNNING = 2;
-
- private void selectView(int which) {
- if (mCurView == which) {
- return;
- }
-
- mCurView = which;
-
- if (which == VIEW_LIST) {
- if (mResumedRunning) {
- mRunningProcessesView.doPause();
- mResumedRunning = false;
- }
- mRunningProcessesView.setVisibility(View.GONE);
- mListView.setVisibility(View.VISIBLE);
- } else if (which == VIEW_RUNNING) {
- if (!mCreatedRunning) {
- mRunningProcessesView.doCreate(null, mNonConfigInstance);
- mCreatedRunning = true;
- }
- if (mActivityResumed && !mResumedRunning) {
- mRunningProcessesView.doResume();
- mResumedRunning = true;
- }
- mRunningProcessesView.setVisibility(View.VISIBLE);
- mListView.setVisibility(View.GONE);
+ @Override
+ public void onMovedToScrapHeap(View view) {
+ mActive.remove(view);
}
}
@@ -1596,11 +370,8 @@ public class ManageApplications extends TabActivity implements
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if(localLOGV) Log.i(TAG, "Activity created");
- long sCreate;
- if (DEBUG_TIME) {
- sCreate = SystemClock.elapsedRealtime();
- }
+ mApplicationsState = ApplicationsState.getInstance(getApplication());
+ mApplicationsAdapter = new ApplicationsAdapter(mApplicationsState);
Intent intent = getIntent();
String action = intent.getAction();
String defaultTabTag = TAB_DOWNLOADED;
@@ -1612,7 +383,6 @@ public class ManageApplications extends TabActivity implements
mSortOrder = SORT_ORDER_SIZE;
mFilterApps = FILTER_APPS_ALL;
defaultTabTag = TAB_ALL;
- mSizesFirst = true;
}
if (savedInstanceState != null) {
@@ -1620,27 +390,19 @@ public class ManageApplications extends TabActivity implements
mFilterApps = savedInstanceState.getInt("filterApps", mFilterApps);
String tmp = savedInstanceState.getString("defaultTabTag");
if (tmp != null) defaultTabTag = tmp;
- mSizesFirst = savedInstanceState.getBoolean("sizesFirst", mSizesFirst);
}
mNonConfigInstance = getLastNonConfigurationInstance();
- mPm = getPackageManager();
// initialize some window features
requestWindowFeature(Window.FEATURE_RIGHT_ICON);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- mDefaultAppIcon = Resources.getSystem().getDrawable(
- com.android.internal.R.drawable.sym_def_app_icon);
mInvalidSizeStr = getText(R.string.invalid_size_value);
mComputingSizeStr = getText(R.string.computing_size);
// initialize the inflater
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mRootView = mInflater.inflate(R.layout.compute_sizes, null);
- mReceiver = new PackageIntentReceiver();
- mObserver = new PkgSizeObserver();
// Create adapter and list view here
- List<ApplicationInfo> appList = getInstalledApps(FILTER_APPS_ALL);
- mAppInfoAdapter = new AppInfoAdapter(this, appList);
ListView lv = (ListView) mRootView.findViewById(android.R.id.list);
lv.setOnItemClickListener(this);
lv.setSaveEnabled(true);
@@ -1648,21 +410,10 @@ public class ManageApplications extends TabActivity implements
lv.setOnItemClickListener(this);
lv.setTextFilterEnabled(true);
mListView = lv;
+ lv.setRecyclerListener(mApplicationsAdapter);
+ mListView.setAdapter(mApplicationsAdapter);
mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
R.id.running_processes);
- if (DEBUG_TIME) {
- Log.i(TAG, "Total time in Activity.create:: " +
- (SystemClock.elapsedRealtime() - sCreate)+ " ms");
- }
- // Get initial info from file for the very first time this activity started
- long sStart;
- if (DEBUG_TIME) {
- sStart = SystemClock.elapsedRealtime();
- }
- mCache.loadCache();
- if (DEBUG_TIME) {
- Log.i(TAG, "Took " + (SystemClock.elapsedRealtime()-sStart) + " ms to init cache");
- }
final TabHost tabHost = getTabHost();
tabHost.addTab(tabHost.newTabSpec(TAB_DOWNLOADED)
@@ -1683,26 +434,18 @@ public class ManageApplications extends TabActivity implements
.setContent(this));
tabHost.setCurrentTabByTag(defaultTabTag);
tabHost.setOnTabChangedListener(this);
-
- selectView(TAB_RUNNING.equals(defaultTabTag) ? VIEW_RUNNING : VIEW_LIST);
}
@Override
public void onStart() {
super.onStart();
- // Register receiver
- mReceiver.registerReceiver();
- sendMessageToHandler(INIT_PKG_INFO);
}
@Override
protected void onResume() {
super.onResume();
mActivityResumed = true;
- if (mCurView == VIEW_RUNNING) {
- mRunningProcessesView.doResume();
- mResumedRunning = true;
- }
+ showCurrentTab();
}
@Override
@@ -1711,7 +454,6 @@ public class ManageApplications extends TabActivity implements
outState.putInt("sortOrder", mSortOrder);
outState.putInt("filterApps", mFilterApps);
outState.putString("defautTabTag", getTabHost().getCurrentTabTag());
- outState.putBoolean("sizesFirst", mSizesFirst);
}
@Override
@@ -1723,6 +465,7 @@ public class ManageApplications extends TabActivity implements
protected void onPause() {
super.onPause();
mActivityResumed = false;
+ mApplicationsAdapter.pause();
if (mResumedRunning) {
mRunningProcessesView.doPause();
mResumedRunning = false;
@@ -1730,284 +473,13 @@ public class ManageApplications extends TabActivity implements
}
@Override
- public void onStop() {
- super.onStop();
- // Stop the background threads
- if (mResourceThread != null) {
- mResourceThread.setAbort();
- }
- if (mSizeComputor != null) {
- mSizeComputor.setAbort();
- }
- // clear all messages related to application list
- clearMessagesInHandler();
- // register receiver here
- unregisterReceiver(mReceiver);
- }
-
- @Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
- // Refresh package attributes
- try {
- ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
- } catch (NameNotFoundException e) {
- Bundle rData = new Bundle();
- rData.putString(ATTR_PKG_NAME, mCurrentPkgName);
- sendMessageToHandler(REMOVE_PKG, rData);
- mCurrentPkgName = null;
- }
+ mApplicationsState.requestSize(mCurrentPkgName);
}
}
- // Avoid the restart and pause when orientation changes
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- }
-
- @Override
- protected void onDestroy() {
- // Persist values in cache
- mCache.updateCache();
- super.onDestroy();
- }
-
- class AppInfoCache {
- final static boolean FILE_CACHE = true;
- private static final String mFileCacheName="ManageAppsInfo.txt";
- private static final int FILE_BUFFER_SIZE = 1024;
- private static final boolean DEBUG_CACHE = false;
- private static final boolean DEBUG_CACHE_TIME = false;
- private Map<String, AppInfo> mAppPropCache = new HashMap<String, AppInfo>();
-
- private boolean isEmpty() {
- return (mAppPropCache.size() == 0);
- }
-
- private AppInfo getEntry(String pkgName) {
- return mAppPropCache.get(pkgName);
- }
-
- private Set<String> getPkgList() {
- return mAppPropCache.keySet();
- }
-
- public void addEntry(AppInfo aInfo) {
- if ((aInfo != null) && (aInfo.pkgName != null)) {
- mAppPropCache.put(aInfo.pkgName, aInfo);
- }
- }
-
- public void removeEntry(String pkgName) {
- if (pkgName != null) {
- mAppPropCache.remove(pkgName);
- }
- }
-
- private void readFromFile() {
- File cacheFile = new File(getFilesDir(), mFileCacheName);
- if (!cacheFile.exists()) {
- return;
- }
- FileInputStream fis = null;
- boolean err = false;
- try {
- fis = new FileInputStream(cacheFile);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Error opening file for read operation : " + cacheFile
- + " with exception " + e);
- return;
- }
- try {
- byte[] byteBuff = new byte[FILE_BUFFER_SIZE];
- byte[] lenBytes = new byte[2];
- mAppPropCache.clear();
- while(fis.available() > 0) {
- fis.read(lenBytes, 0, 2);
- int buffLen = (lenBytes[0] << 8) | lenBytes[1];
- if ((buffLen <= 0) || (buffLen > byteBuff.length)) {
- err = true;
- break;
- }
- // Buffer length cannot be greater than max.
- fis.read(byteBuff, 0, buffLen);
- String buffStr = new String(byteBuff);
- if (DEBUG_CACHE) {
- Log.i(TAG, "Read string of len= " + buffLen + " :: " + buffStr + " from file");
- }
- // Parse string for sizes
- String substrs[] = buffStr.split(",");
- if (substrs.length < 4) {
- // Something wrong. Bail out and let recomputation proceed.
- err = true;
- break;
- }
- long size = -1;
- int idx = -1;
- try {
- size = Long.parseLong(substrs[1]);
- } catch (NumberFormatException e) {
- err = true;
- break;
- }
- if (DEBUG_CACHE) {
- Log.i(TAG, "Creating entry(" + substrs[0] + ", " + idx+"," + size + ", " + substrs[2] + ")");
- }
- AppInfo aInfo = new AppInfo(substrs[0], idx, substrs[3], size, substrs[2]);
- mAppPropCache.put(aInfo.pkgName, aInfo);
- }
- } catch (IOException e) {
- Log.w(TAG, "Failed reading from file : " + cacheFile + " with exception : " + e);
- err = true;
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
- Log.w(TAG, "Failed to close file " + cacheFile + " with exception : " +e);
- err = true;
- }
- }
- if (err) {
- Log.i(TAG, "Failed to load cache. Not using cache for now.");
- // Clear cache and bail out
- mAppPropCache.clear();
- }
- }
- }
-
- boolean writeToFile() {
- File cacheFile = new File(getFilesDir(), mFileCacheName);
- FileOutputStream fos = null;
- try {
- long opStartTime = SystemClock.uptimeMillis();
- fos = new FileOutputStream(cacheFile);
- Set<String> keys = mAppPropCache.keySet();
- byte[] lenBytes = new byte[2];
- for (String key : keys) {
- AppInfo aInfo = mAppPropCache.get(key);
- StringBuilder buff = new StringBuilder(aInfo.pkgName);
- buff.append(",");
- buff.append(aInfo.size);
- buff.append(",");
- buff.append(aInfo.appSize);
- buff.append(",");
- buff.append(aInfo.appName);
- if (DEBUG_CACHE) {
- Log.i(TAG, "Writing str : " + buff.toString() + " to file of length:" +
- buff.toString().length());
- }
- try {
- byte[] byteBuff = buff.toString().getBytes();
- int len = byteBuff.length;
- if (byteBuff.length >= FILE_BUFFER_SIZE) {
- // Truncate the output
- len = FILE_BUFFER_SIZE;
- }
- // Use 2 bytes to write length
- lenBytes[1] = (byte) (len & 0x00ff);
- lenBytes[0] = (byte) ((len & 0x00ff00) >> 8);
- fos.write(lenBytes, 0, 2);
- fos.write(byteBuff, 0, len);
- } catch (IOException e) {
- Log.w(TAG, "Failed to write to file : " + cacheFile + " with exception : " + e);
- return false;
- }
- }
- if (DEBUG_CACHE_TIME) {
- Log.i(TAG, "Took " + (SystemClock.uptimeMillis() - opStartTime) + " ms to write and process from file");
- }
- return true;
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Error opening file for write operation : " + cacheFile+
- " with exception : " + e);
- return false;
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException e) {
- Log.w(TAG, "Failed closing file : " + cacheFile + " with exception : " + e);
- return false;
- }
- }
- }
- }
- private void loadCache() {
- // Restore preferences
- SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
- boolean disable = settings.getBoolean(PREF_DISABLE_CACHE, true);
- if (disable) Log.w(TAG, "Cache has been disabled");
- // Disable cache till the data is loaded successfully
- SharedPreferences.Editor editor = settings.edit();
- editor.putBoolean(PREF_DISABLE_CACHE, true);
- editor.commit();
- if (FILE_CACHE && !disable) {
- readFromFile();
- // Enable cache since the file has been read successfully
- editor.putBoolean(PREF_DISABLE_CACHE, false);
- editor.commit();
- }
- }
-
- private void updateCache() {
- SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
- SharedPreferences.Editor editor = settings.edit();
- editor.putBoolean(PREF_DISABLE_CACHE, true);
- editor.commit();
- if (FILE_CACHE) {
- boolean writeStatus = writeToFile();
- mAppPropCache.clear();
- if (writeStatus) {
- // Enable cache since the file has been read successfully
- editor.putBoolean(PREF_DISABLE_CACHE, false);
- editor.commit();
- }
- }
- }
- }
-
- /*
- * comparator class used to sort AppInfo objects based on size
- */
- class SizeComparator implements Comparator<ApplicationInfo> {
- public final int compare(ApplicationInfo a, ApplicationInfo b) {
- AppInfo ainfo = mCache.getEntry(a.packageName);
- AppInfo binfo = mCache.getEntry(b.packageName);
- long atotal = ainfo.size;
- long btotal = binfo.size;
- long ret = atotal - btotal;
- // negate result to sort in descending order
- if (ret < 0) {
- return 1;
- }
- if (ret == 0) {
- return 0;
- }
- return -1;
- }
- }
-
- /*
- * Customized comparator class to compare labels.
- * Don't use the one defined in ApplicationInfo since that loads the labels again.
- */
- class AlphaComparator implements Comparator<ApplicationInfo> {
- private final Collator sCollator = Collator.getInstance();
-
- public final int compare(ApplicationInfo a, ApplicationInfo b) {
- AppInfo ainfo = mCache.getEntry(a.packageName);
- AppInfo binfo = mCache.getEntry(b.packageName);
- // Check for null app names, to avoid NPE in rare cases
- if (ainfo == null || ainfo.appName == null) return -1;
- if (binfo == null || binfo.appName == null) return 1;
- return sCollator.compare(ainfo.appName.toString(), binfo.appName.toString());
- }
- }
-
// utility method used to start sub activity
private void startApplicationDetailsActivity() {
// Create intent to start new activity
@@ -2028,27 +500,25 @@ public class ManageApplications extends TabActivity implements
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- if (mFirst) {
- menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
- menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
- return true;
- }
- return false;
+ menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
+ menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
+ return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int menuId = item.getItemId();
if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
- sendMessageToHandler(REORDER_LIST, menuId);
+ mSortOrder = menuId;
+ mApplicationsAdapter.rebuild(mFilterApps, mSortOrder);
}
return true;
}
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
- ApplicationInfo info = (ApplicationInfo)mAppInfoAdapter.getItem(position);
- mCurrentPkgName = info.packageName;
+ ApplicationsState.AppEntry entry = mApplicationsAdapter.getAppEntry(position);
+ mCurrentPkgName = entry.info.packageName;
startApplicationDetailsActivity();
}
@@ -2061,7 +531,43 @@ public class ManageApplications extends TabActivity implements
return mRootView;
}
- public void onTabChanged(String tabId) {
+ static final int VIEW_NOTHING = 0;
+ static final int VIEW_LIST = 1;
+ static final int VIEW_RUNNING = 2;
+
+ private void selectView(int which) {
+ if (which == VIEW_LIST) {
+ if (mResumedRunning) {
+ mRunningProcessesView.doPause();
+ mResumedRunning = false;
+ }
+ if (mCurView != which) {
+ mRunningProcessesView.setVisibility(View.GONE);
+ mListView.setVisibility(View.VISIBLE);
+ }
+ if (mActivityResumed) {
+ mApplicationsAdapter.resume(mFilterApps, mSortOrder);
+ }
+ } else if (which == VIEW_RUNNING) {
+ if (!mCreatedRunning) {
+ mRunningProcessesView.doCreate(null, mNonConfigInstance);
+ mCreatedRunning = true;
+ }
+ if (mActivityResumed && !mResumedRunning) {
+ mRunningProcessesView.doResume();
+ mResumedRunning = true;
+ }
+ mApplicationsAdapter.pause();
+ if (mCurView != which) {
+ mRunningProcessesView.setVisibility(View.VISIBLE);
+ mListView.setVisibility(View.GONE);
+ }
+ }
+ mCurView = which;
+ }
+
+ public void showCurrentTab() {
+ String tabId = getTabHost().getCurrentTabTag();
int newOption;
if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_THIRD_PARTY;
@@ -2077,7 +583,11 @@ public class ManageApplications extends TabActivity implements
return;
}
+ mFilterApps = newOption;
selectView(VIEW_LIST);
- sendMessageToHandler(REORDER_LIST, newOption);
+ }
+
+ public void onTabChanged(String tabId) {
+ showCurrentTab();
}
}
diff --git a/src/com/android/settings/applications/RunningProcessesView.java b/src/com/android/settings/applications/RunningProcessesView.java
index d63cc88..580ae05 100644
--- a/src/com/android/settings/applications/RunningProcessesView.java
+++ b/src/com/android/settings/applications/RunningProcessesView.java
@@ -45,6 +45,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
+import android.widget.AbsListView.RecyclerListener;
import java.io.FileInputStream;
import java.util.ArrayList;
@@ -52,7 +53,7 @@ import java.util.HashMap;
import java.util.Iterator;
public class RunningProcessesView extends FrameLayout
- implements AdapterView.OnItemClickListener {
+ implements AdapterView.OnItemClickListener, RecyclerListener {
/** Maximum number of services to retrieve */
static final int MAX_SERVICES = 100;
@@ -518,6 +519,7 @@ public class RunningProcessesView extends FrameLayout
mListView.setEmptyView(emptyView);
}
mListView.setOnItemClickListener(this);
+ mListView.setRecyclerListener(this);
mListView.setAdapter(new ServiceListAdapter(mState));
mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);