summaryrefslogtreecommitdiffstats
path: root/src/com/android/settings/ManageApplications.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:06:01 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:06:01 -0800
commitabc48f80d8747b4fc051b7dd364355ee667a9bac (patch)
tree31ae577fe29d75963b071e738703e4db83ad6580 /src/com/android/settings/ManageApplications.java
parentde2d9f5f109265873196f1615e1f3546b114aaa7 (diff)
downloadpackages_apps_Settings-abc48f80d8747b4fc051b7dd364355ee667a9bac.tar.gz
packages_apps_Settings-abc48f80d8747b4fc051b7dd364355ee667a9bac.tar.bz2
packages_apps_Settings-abc48f80d8747b4fc051b7dd364355ee667a9bac.zip
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'src/com/android/settings/ManageApplications.java')
-rw-r--r--src/com/android/settings/ManageApplications.java1321
1 files changed, 935 insertions, 386 deletions
diff --git a/src/com/android/settings/ManageApplications.java b/src/com/android/settings/ManageApplications.java
index 838950251..f1550f978 100644
--- a/src/com/android/settings/ManageApplications.java
+++ b/src/com/android/settings/ManageApplications.java
@@ -18,8 +18,12 @@ package com.android.settings;
import com.android.settings.R;
import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
@@ -27,320 +31,954 @@ 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.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.text.format.Formatter;
import android.util.Config;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
import android.widget.AdapterView;
+import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
-import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
/**
* Activity to pick an application that will be used to display installation information and
- * options to upgrade/uninstall/delete user data for system applications.
+ * 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 size information of installed packages which is done asynchronously through a
- * handler. Once the computation is done package resource information is retrieved
- * and then the information is displayed on the screen. All
- * messages are passed through a Handler object.
- * Known issue: There could be some ordering issues when installing/uninstalling
- * applications when the application list is being scanned.
+ * 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.
+ * This activity passes the package name and size information to the
+ * InstalledAppDetailsActivity to avoid recomputation of the package size information.
*/
-public class ManageApplications extends Activity implements SimpleAdapter.ViewBinder, OnItemClickListener {
+public class ManageApplications extends ListActivity implements
+ OnItemClickListener, DialogInterface.OnCancelListener {
+ // TAG for this activity
private static final String TAG = "ManageApplications";
- //Application prefix information
- public static final String APP_PKG_PREFIX="com.android.settings.";
- public static final String APP_PKG_NAME=APP_PKG_PREFIX+"ApplicationPkgName";
- public static final String APP_PKG_SIZE= APP_PKG_PREFIX+"size";
- public static final String APP_CHG=APP_PKG_PREFIX+"changed";
- //constant value that can be used to check return code from sub activity.
- private static final int INSTALLED_APP_DETAILS = 1;
- //application attributes passed to sub activity that displays more app info
- private static final String KEY_APP_NAME = "ApplicationName";
- private static final String KEY_APP_ICON = "ApplicationIcon";
- private static final String KEY_APP_DESC = "ApplicationDescription";
- private static final String KEY_APP_SIZE= "ApplicationSize";
- //sort order that can be changed through the menu
- public static final int SORT_ORDER_ALPHA = 0;
- public static final int SORT_ORDER_SIZE = 1;
- //key and resource values used in constructing map for SimpleAdapter
- private static final String sKeys[] = new String[] { KEY_APP_NAME, KEY_APP_ICON,
- KEY_APP_DESC, KEY_APP_SIZE};
- private static final int sResourceIds[] = new int[] { R.id.app_name, R.id.app_icon,
- R.id.app_description, R.id.app_size};
- //List of ApplicationInfo objects for various applications
- private List<ApplicationInfo> mAppList;
- //SimpleAdapter used for managing items in the list
- private SimpleAdapter mAppAdapter;
- //map used to store size information which is used for displaying size information
- //in this activity as well as the subactivity. this is to avoid invoking package manager
- //api to retrieve size information
- private HashMap<String, PackageStats> mSizeMap;
- private HashMap<String, Map<String, ?> > mAppAdapterMap;
- //sort order
- private int mSortOrder = SORT_ORDER_ALPHA;
- //log information boolean
+ // log information boolean
private boolean localLOGV = Config.LOGV || false;
- private ApplicationInfo mCurrentPkg;
- private int mCurrentPkgIdx = 0;
- private static final int COMPUTE_PKG_SIZE_START = 1;
- private static final int COMPUTE_PKG_SIZE_DONE = 2;
- private static final int REMOVE_PKG=3;
- private static final int REORDER_LIST=4;
- private static final int ADD_PKG=5;
- private static final String ATTR_APP_IDX="ApplicationIndex";
- private static final String ATTR_CHAINED="Chained";
+
+ // attributes used as keys when passing values to InstalledAppDetails activity
+ public static final String APP_PKG_PREFIX = "com.android.settings.";
+ public static final String APP_PKG_NAME = APP_PKG_PREFIX+"ApplicationPkgName";
+ public static final String APP_PKG_SIZE = APP_PKG_PREFIX+"size";
+ public static final String APP_CHG = APP_PKG_PREFIX+"changed";
+
+ // attribute name used in receiver for tagging names of added/deleted packages
private static final String ATTR_PKG_NAME="PackageName";
+ private static final String ATTR_APP_PKG_STATS="ApplicationPackageStats";
+
+ // constant value that can be used to check return code from sub activity.
+ private static final int INSTALLED_APP_DETAILS = 1;
+
+ // sort order that can be changed through the menu can be sorted alphabetically
+ // or size(descending)
+ private static final int MENU_OPTIONS_BASE = 0;
+ public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 0;
+ public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 1;
+ // Filter options used for displayed list of applications
+ public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 2;
+ public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 3;
+ public static final int FILTER_APPS_RUNNING = MENU_OPTIONS_BASE + 4;
+ // sort order
+ private int mSortOrder = SORT_ORDER_ALPHA;
+ // Filter value
+ int mFilterApps = FILTER_APPS_ALL;
+
+ // 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 COMPUTE_PKG_SIZE_START = HANDLER_MESSAGE_BASE+1;
+ private static final int COMPUTE_PKG_SIZE_DONE = 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_ICONS = HANDLER_MESSAGE_BASE+7;
+
+ // 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 mDoneIniting = false;
- private String mKbStr;
- private String mMbStr;
- private String mBStr;
+ // default icon thats used when displaying applications initially before resource info is
+ // retrieved
+ private Drawable mDefaultAppIcon;
+
+ // temporary dialog displayed while the application info loads
+ private ProgressDialog mLoadingDlg = null;
+
+ // compute index used to track the application size computations
+ private int mComputeIndex;
+
+ // 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;
+
+ String mCurrentPkgName;
+
+ //TODO implement a cache system
+ private Map<String, AppInfo> mAppPropCache;
/*
* 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., is 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) {
PackageStats ps;
ApplicationInfo info;
Bundle data;
- String pkgName;
- int idx;
- int size;
- boolean chained = false;
+ String pkgName = null;
+ AppInfo appInfo;
data = msg.getData();
+ if(data != null) {
+ pkgName = data.getString(ATTR_PKG_NAME);
+ }
switch (msg.what) {
case COMPUTE_PKG_SIZE_START:
- mDoneIniting = false;
- //initialize lists
- mAppList = new ArrayList<ApplicationInfo>();
- mSizeMap = new HashMap<String, PackageStats>();
- mAppAdapterMap = new HashMap<String, Map<String, ?> >();
- //update application list from PackageManager
- mAppList = mPm.getInstalledApplications(0);
- if(mAppList.size() == 0) {
- return;
- }
- mCurrentPkgIdx = 0;
- mCurrentPkg = mAppList.get(0);
- if(localLOGV) Log.i(TAG, "Initiating compute sizes for first time");
- //register receiver
- mReceiver = new PackageIntentReceiver();
- mReceiver.registerReceiver();
- pkgName = mCurrentPkg.packageName;
- mObserver = new PkgSizeObserver(0);
- mObserver.invokeGetSizeInfo(pkgName, true);
+ if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_START");
+ setProgressBarIndeterminateVisibility(true);
+ mComputeIndex = 0;
+ initAppList(mFilterApps);
break;
case COMPUTE_PKG_SIZE_DONE:
- ps = mObserver.ps;
- info = mObserver.appInfo;
- chained = data.getBoolean(ATTR_CHAINED);
- if(!mObserver.succeeded) {
- if(chained) {
- removePackageFromAppList(ps.packageName);
- } else {
- //do not go to adding phase
+ if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
+ if(pkgName == null) {
+ Log.w(TAG, "Ignoring message");
+ break;
+ }
+ ps = data.getParcelable(ATTR_APP_PKG_STATS);
+ if(ps == null) {
+ Log.i(TAG, "Invalid package stats for package:"+pkgName);
+ } else {
+ int pkgId = mAppInfoAdapter.getIndex(pkgName);
+ if(mComputeIndex != pkgId) {
+ //spurious call from stale observer
+ Log.w(TAG, "Stale call back from PkgSizeObserver");
break;
}
- } else {
- //insert size value
- mSizeMap.put(ps.packageName, ps);
- Map<String, Object> entry = createMapEntry(mPm.getApplicationLabel(info),
- mPm.getApplicationIcon(info),
- info.loadDescription(mPm),
- getSizeStr(ps));
- mAppAdapterMap.put(ps.packageName, entry);
+ mAppInfoAdapter.updateAppSize(pkgName, ps);
}
- if(chained) {
- //here app list is precomputed
- idx = data.getInt(ATTR_APP_IDX);
- //increment only if succeded
- if(mObserver.succeeded) {
- idx++;
+ mComputeIndex++;
+ if (mComputeIndex < mAppInfoAdapter.getCount()) {
+ // initiate compute package size for next pkg in list
+ mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
+ mComputeIndex),
+ COMPUTE_PKG_SIZE_DONE);
+ } else {
+ // check for added/removed packages
+ Set<String> keys = mAddRemoveMap.keySet();
+ Iterator<String> iter = keys.iterator();
+ List<String> removeList = new ArrayList<String>();
+ boolean added = false;
+ boolean removed = false;
+ while (iter.hasNext()) {
+ String key = iter.next();
+ if (mAddRemoveMap.get(key) == Boolean.TRUE) {
+ // add
+ try {
+ info = mPm.getApplicationInfo(key, 0);
+ mAppInfoAdapter.addApplicationInfo(info);
+ added = true;
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Invalid added package:"+key+" Ignoring entry");
+ }
+ } else {
+ // remove
+ removeList.add(key);
+ removed = true;
+ }
}
- if(idx < mAppList.size()) {
- pkgName = mAppList.get(idx).packageName;
- //increment record index and invoke getSizeInfo for next record
- mObserver.invokeGetSizeInfo(pkgName, true);
+ // remove uninstalled packages from list
+ if (removed) {
+ mAppInfoAdapter.removeFromList(removeList);
+ }
+ // handle newly installed packages
+ if (added) {
+ mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
+ mComputeIndex),
+ COMPUTE_PKG_SIZE_DONE);
} else {
- sortAppList();
- createListFromValues();
+ // end computation here
mDoneIniting = true;
- }
- } else {
- //add app info object as well
- mAppList.add(info);
- sortAppList();
- size = mAppList.size();
- int i;
- for(i = 0; i < size; i++) {
- if(mAppList.get(i).packageName.equalsIgnoreCase(mCurrentPkg.packageName)) {
- if(i > mCurrentPkgIdx) {
- mCurrentPkgIdx = i;
- }
- break;
+ mAppInfoAdapter.sortList(mSortOrder);
+ //load resources now
+ if(mResourceThread.isAlive()) {
+ mResourceThread.interrupt();
}
+ mResourceThread.loadAllResources(mAppInfoAdapter.getAppList());
}
- createListFromValues();
}
break;
case REMOVE_PKG:
- if(!mDoneIniting) {
- //insert message again after some delay
- sendMessageToHandler(REMOVE_PKG, data, 10*1000);
+ if(localLOGV) Log.i(TAG, "Message REMOVE_PKG");
+ if(pkgName == null) {
+ Log.w(TAG, "Ignoring message:REMOVE_PKG for null pkgName");
break;
}
- pkgName = data.getString(ATTR_PKG_NAME);
- removePackageFromAppList(pkgName);
- if(mSizeMap.remove(pkgName) == null) {
- Log.i(TAG, "Coudnt remove from size map package:"+pkgName);
- }
- if(mAppAdapterMap.remove(pkgName) == null) {
- Log.i(TAG, "Coudnt remove from app adapter map package:"+pkgName);
- }
- if(mCurrentPkg.packageName.equalsIgnoreCase(pkgName)) {
- if(mCurrentPkgIdx == (mAppList.size()-1)) {
- mCurrentPkgIdx--;
+ if (!mDoneIniting) {
+ Boolean currB = mAddRemoveMap.get(pkgName);
+ if (currB == null || (currB.equals(Boolean.TRUE))) {
+ mAddRemoveMap.put(pkgName, Boolean.FALSE);
}
- mCurrentPkg = mAppList.get(mCurrentPkgIdx);
+ break;
}
- createListFromValues();
+ List<String> pkgList = new ArrayList<String>();
+ pkgList.add(pkgName);
+ mAppInfoAdapter.removeFromList(pkgList);
break;
case REORDER_LIST:
- int sortOrder = msg.arg1;
- if(sortOrder != mSortOrder) {
- mSortOrder = sortOrder;
- if(localLOGV) Log.i(TAG, "Changing sort order to "+mSortOrder);
- sortAppList();
- mCurrentPkgIdx = 0;
- mCurrentPkg = mAppList.get(mCurrentPkgIdx);
- createListFromValues();
+ 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,
+ getInstalledApps(mFilterApps));
+ if(!ret) {
+ // Reset cache
+ mAppPropCache = null;
+ mFilterApps = FILTER_APPS_ALL;
+ mHandler.sendEmptyMessage(COMPUTE_PKG_SIZE_START);
+ sendMessageToHandler(REORDER_LIST, menuOption);
+ }
}
break;
- case ADD_PKG:
- pkgName = data.getString(ATTR_PKG_NAME);
- if(!mDoneIniting) {
- //insert message again after some delay
- sendMessageToHandler(ADD_PKG, data, 10*1000);
+ 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;
}
- mObserver.invokeGetSizeInfo(pkgName, false);
+ if (!mDoneIniting) {
+ Boolean currB = mAddRemoveMap.get(pkgName);
+ if (currB == null || (currB.equals(Boolean.FALSE))) {
+ mAddRemoveMap.put(pkgName, Boolean.TRUE);
+ }
+ break;
+ }
+ try {
+ info = mPm.getApplicationInfo(pkgName, 0);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Couldnt find application info for:"+pkgName);
+ break;
+ }
+ mObserver.invokeGetSizeInfo(info, ADD_PKG_DONE);
break;
+ case ADD_PKG_DONE:
+ if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
+ if(pkgName == null) {
+ Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
+ break;
+ }
+ ps = data.getParcelable(ATTR_APP_PKG_STATS);
+ mAppInfoAdapter.addToList(pkgName, ps);
+ break;
+ case REFRESH_ICONS:
+ Map<String, AppInfo> iconMap = (Map<String, AppInfo>) msg.obj;
+ if(iconMap == null) {
+ Log.w(TAG, "Error loading icons for applications");
+ } else {
+ mAppInfoAdapter.updateAppsResourceInfo(iconMap);
+ setProgressBarIndeterminateVisibility(false);
+ }
default:
break;
}
}
};
- private void removePackageFromAppList(String pkgName) {
- int size = mAppList.size();
- for(int i = 0; i < size; i++) {
- if(mAppList.get(i).packageName.equalsIgnoreCase(pkgName)) {
- mAppList.remove(i);
- break;
+ List<ApplicationInfo> getInstalledApps(int filterOption) {
+ List<ApplicationInfo> installedAppList = mPm.getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ if (installedAppList == null) {
+ return new ArrayList<ApplicationInfo> ();
+ }
+ if (filterOption == FILTER_APPS_THIRD_PARTY) {
+ List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
+ for (ApplicationInfo appInfo : installedAppList) {
+ if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ appList.add(appInfo);
+ }
+ }
+ return appList;
+ } else if (filterOption == FILTER_APPS_RUNNING) {
+ List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
+ List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
+ if ((procList == null) || (procList.size() == 0)) {
+ return appList;
}
+ // Retrieve running processes from ActivityManager
+ for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
+ if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
+ int size = appProcInfo.pkgList.length;
+ for (int i = 0; i < size; i++) {
+ ApplicationInfo appInfo = null;
+ try {
+ appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i],
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]);
+ continue;
+ }
+ if(appInfo != null) {
+ appList.add(appInfo);
+ }
+ }
+ }
+ }
+ return appList;
+ } else {
+ return installedAppList;
}
}
- private void clearMessages() {
- synchronized(mHandler) {
- mHandler.removeMessages(COMPUTE_PKG_SIZE_START);
- mHandler.removeMessages(COMPUTE_PKG_SIZE_DONE);
- mHandler.removeMessages(REMOVE_PKG);
- mHandler.removeMessages(REORDER_LIST);
- mHandler.removeMessages(ADD_PKG);
+ private List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesList() {
+ ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
+ return am.getRunningAppProcesses();
+ }
+
+ // some initialization code used when kicking off the size computation
+ private void initAppList(int filterOption) {
+ mDoneIniting = false;
+ // Initialize lists
+ List<ApplicationInfo> appList = getInstalledApps(filterOption);
+ mAddRemoveMap = new TreeMap<String, Boolean>();
+ mAppInfoAdapter = new AppInfoAdapter(this, appList);
+ dismissLoadingMsg();
+ // get list and set listeners and adapter
+ ListView lv= (ListView) findViewById(android.R.id.list);
+ lv.setOnItemClickListener(this);
+ lv.setSaveEnabled(true);
+ lv.setItemsCanFocus(true);
+ lv.setOnItemClickListener(this);
+ lv.setAdapter(mAppInfoAdapter);
+ // register receiver
+ mReceiver = new PackageIntentReceiver();
+ mReceiver.registerReceiver();
+ // initiate compute pkg sizes
+ if (localLOGV) Log.i(TAG, "Initiating compute sizes for first time");
+ mObserver = new PkgSizeObserver();
+ if(appList.size() > 0) {
+ mObserver.invokeGetSizeInfo(appList.get(0), COMPUTE_PKG_SIZE_DONE);
+ } else {
+ mDoneIniting = true;
}
}
- private void sendMessageToHandler(int msgId, Bundle data, long delayMillis) {
- synchronized(mHandler) {
- Message msg = mHandler.obtainMessage(msgId);
- msg.setData(data);
- if(delayMillis == 0) {
- mHandler.sendMessage(msg);
- } else {
- mHandler.sendMessageDelayed(msg, delayMillis);
- }
+ // internal structure used to track added and deleted packages when
+ // the activity has focus
+ class AddRemoveInfo {
+ String pkgName;
+ boolean add;
+ public AddRemoveInfo(String pPkgName, boolean pAdd) {
+ pkgName = pPkgName;
+ add = pAdd;
}
}
- private void sendMessageToHandler(int msgId, int arg1) {
- synchronized(mHandler) {
- Message msg = mHandler.obtainMessage(msgId);
- msg.arg1 = arg1;
+ class ResourceLoaderThread extends Thread {
+ List<ApplicationInfo> mAppList;
+
+ void loadAllResources(List<ApplicationInfo> appList) {
+ if(appList == null || appList.size() <= 0) {
+ Log.w(TAG, "Empty or null application list");
+ return;
+ }
+ mAppList = appList;
+ start();
+ }
+
+ public void run() {
+ Map<String, AppInfo> iconMap = new HashMap<String, AppInfo>();
+ for (ApplicationInfo appInfo : mAppList) {
+ CharSequence appName = appInfo.loadLabel(mPm);
+ Drawable appIcon = appInfo.loadIcon(mPm);
+ iconMap.put(appInfo.packageName,
+ new AppInfo(appInfo.packageName, appName, appIcon));
+ }
+ Message msg = mHandler.obtainMessage(REFRESH_ICONS);
+ msg.obj = iconMap;
mHandler.sendMessage(msg);
}
}
- private void sendMessageToHandler(int msgId) {
- synchronized(mHandler) {
- mHandler.sendEmptyMessage(msgId);
+ /* Internal class representing an application or packages displayable attributes
+ *
+ */
+ class AppInfo {
+ public String pkgName;
+ int index;
+ public CharSequence appName;
+ public Drawable appIcon;
+ public CharSequence appSize;
+ public PackageStats appStats;
+
+ public void refreshIcon(AppInfo pInfo) {
+ appName = pInfo.appName;
+ appIcon = pInfo.appIcon;
+ }
+
+ public AppInfo(String pName, CharSequence aName, Drawable aIcon) {
+ index = -1;
+ pkgName = pName;
+ appName = aName;
+ appIcon = aIcon;
+ appStats = null;
+ appSize = mComputingSizeStr;
+ }
+
+ public AppInfo(String pName, int pIndex, CharSequence aName, Drawable aIcon,
+ PackageStats ps) {
+ index = pIndex;
+ pkgName = pName;
+ appName = aName;
+ appIcon = aIcon;
+ if(ps == null) {
+ appSize = mComputingSizeStr;
+ } else {
+ appStats = ps;
+ appSize = getSizeStr();
+ }
+ }
+ public void setSize(PackageStats ps) {
+ appStats = ps;
+ if (ps != null) {
+ appSize = getSizeStr();
+ }
+ }
+ public long getTotalSize() {
+ PackageStats ps = appStats;
+ if (ps != null) {
+ return ps.cacheSize+ps.codeSize+ps.dataSize;
+ }
+ return SIZE_INVALID;
+ }
+
+ private String getSizeStr() {
+ PackageStats ps = appStats;
+ String retStr = "";
+ // insert total size information into map to display in view
+ // at this point its guaranteed that ps is not null. but checking anyway
+ if (ps != null) {
+ long size = getTotalSize();
+ if (size == SIZE_INVALID) {
+ return mInvalidSizeStr.toString();
+ }
+ return Formatter.formatFileSize(ManageApplications.this, size);
+ }
+ return retStr;
}
}
- class PkgSizeObserver extends IPackageStatsObserver.Stub {
- public PackageStats ps;
- public ApplicationInfo appInfo;
- public Drawable appIcon;
- public CharSequence appName;
- public CharSequence appDesc = "";
- private int mIdx = 0;
- private boolean mChained = false;
- public boolean succeeded;
- PkgSizeObserver(int i) {
- mIdx = i;
+ // View Holder used when displaying views
+ static class AppViewHolder {
+ TextView appName;
+ ImageView appIcon;
+ TextView appSize;
+ }
+
+ /* 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 {
+ private Map<String, AppInfo> mAppPropMap;
+ private List<ApplicationInfo> mAppLocalList;
+ ApplicationInfo.DisplayNameComparator mAlphaComparator;
+ AppInfoComparator mSizeComparator;
+
+ private AppInfo getFromCache(String packageName) {
+ if(mAppPropCache == null) {
+ return null;
+ }
+ return mAppPropCache.get(packageName);
}
- private void getAppDetails() {
+ public AppInfoAdapter(Context c, List<ApplicationInfo> appList) {
+ mAppLocalList = appList;
+ boolean useCache = false;
+ int sortOrder = SORT_ORDER_ALPHA;
+ int imax = mAppLocalList.size();
+ if(mAppPropCache != null) {
+ useCache = true;
+ // Activity has been resumed. can use the cache to populate values initially
+ mAppPropMap = mAppPropCache;
+ sortOrder = mSortOrder;
+ }
+ sortAppList(sortOrder);
+ // Recreate property map
+ mAppPropMap = new TreeMap<String, AppInfo>();
+ for (int i = 0; i < imax; i++) {
+ ApplicationInfo info = mAppLocalList.get(i);
+ AppInfo aInfo = getFromCache(info.packageName);
+ if(aInfo == null){
+ aInfo = new AppInfo(info.packageName, i,
+ info.packageName, mDefaultAppIcon, null);
+ } else {
+ aInfo.index = i;
+ }
+ mAppPropMap.put(info.packageName, aInfo);
+ }
+ }
+
+ public int getCount() {
+ return mAppLocalList.size();
+ }
+
+ public Object getItem(int position) {
+ return mAppLocalList.get(position);
+ }
+
+ /*
+ * This method returns the index of the package position in the application list
+ */
+ public int getIndex(String pkgName) {
+ if(pkgName == null) {
+ Log.w(TAG, "Getting index of null package in List Adapter");
+ }
+ int imax = mAppLocalList.size();
+ ApplicationInfo appInfo;
+ for(int i = 0; i < imax; i++) {
+ appInfo = mAppLocalList.get(i);
+ if(appInfo.packageName.equalsIgnoreCase(pkgName)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ 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 void addApplicationInfo(ApplicationInfo info) {
+ if(info == null) {
+ Log.w(TAG, "Ignoring null add in List Adapter");
+ return;
+ }
+ mAppLocalList.add(info);
+ }
+
+ 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;
+ }
+ return mAppPropMap.get(mAppLocalList.get(position).packageName).index;
+ }
+
+ public List<ApplicationInfo> getAppList() {
+ return mAppLocalList;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // A ViewHolder keeps references to children views to avoid unneccessary calls
+ // to findViewById() on each row.
+ AppViewHolder holder;
+
+ // When convertView is not null, we can reuse it directly, there is no need
+ // to reinflate it. We only inflate a new View when the convertView supplied
+ // by ListView is null.
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.manage_applications_item, null);
+
+ // Creates a ViewHolder and store references to the two children views
+ // we want to bind data to.
+ holder = new AppViewHolder();
+ holder.appName = (TextView) convertView.findViewById(R.id.app_name);
+ holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
+ holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
+ convertView.setTag(holder);
+ } else {
+ // Get the ViewHolder back to get fast access to the TextView
+ // and the ImageView.
+ holder = (AppViewHolder) convertView.getTag();
+ }
+
+ // Bind the data efficiently with the holder
+ ApplicationInfo appInfo = mAppLocalList.get(position);
+ AppInfo mInfo = mAppPropMap.get(appInfo.packageName);
+ if(mInfo != null) {
+ if(mInfo.appName != null) {
+ holder.appName.setText(mInfo.appName);
+ }
+ if(mInfo.appIcon != null) {
+ holder.appIcon.setImageDrawable(mInfo.appIcon);
+ }
+ holder.appSize.setText(mInfo.appSize);
+ } else {
+ Log.w(TAG, "No info for package:"+appInfo.packageName+" in property map");
+ }
+ return convertView;
+ }
+
+ private void adjustIndex() {
+ int imax = mAppLocalList.size();
+ ApplicationInfo info;
+ for (int i = 0; i < imax; i++) {
+ info = mAppLocalList.get(i);
+ mAppPropMap.get(info.packageName).index = i;
+ }
+ }
+
+ public void sortAppList(int sortOrder) {
+ Collections.sort(mAppLocalList, getAppComparator(sortOrder));
+ }
+
+ public void sortList(int sortOrder) {
+ sortAppList(sortOrder);
+ adjustIndex();
+ notifyDataSetChanged();
+ }
+
+ public boolean resetAppList(int filterOption, List<ApplicationInfo> appList) {
+ // Create application list based on the filter value
+ mAppLocalList = appList;
+ // Check for all properties in map before sorting. Populate values from cache
+ for(ApplicationInfo applicationInfo : mAppLocalList) {
+ AppInfo appInfo = mAppPropMap.get(applicationInfo.packageName);
+ if(appInfo == null) {
+ AppInfo rInfo = getFromCache(applicationInfo.packageName);
+ if(rInfo == null) {
+ // Need to load resources again. Inconsistency somewhere
+ return false;
+ }
+ mAppPropMap.put(applicationInfo.packageName, rInfo);
+ }
+ }
+ sortList(mSortOrder);
+ return true;
+ }
+
+ private Comparator<ApplicationInfo> getAppComparator(int sortOrder) {
+ if (sortOrder == SORT_ORDER_ALPHA) {
+ // Lazy initialization
+ if (mAlphaComparator == null) {
+ mAlphaComparator = new ApplicationInfo.DisplayNameComparator(mPm);
+ }
+ return mAlphaComparator;
+ }
+ // Lazy initialization
+ if(mSizeComparator == null) {
+ mSizeComparator = new AppInfoComparator(mAppPropMap);
+ }
+ return mSizeComparator;
+ }
+
+ public void updateAppsResourceInfo(Map<String, AppInfo> iconMap) {
+ if(iconMap == null) {
+ Log.w(TAG, "Null iconMap when refreshing icon in List Adapter");
+ return;
+ }
+ boolean changed = false;
+ for (ApplicationInfo info : mAppLocalList) {
+ AppInfo pInfo = iconMap.get(info.packageName);
+ if(pInfo != null) {
+ AppInfo aInfo = mAppPropMap.get(info.packageName);
+ aInfo.refreshIcon(pInfo);
+ changed = true;
+ }
+ }
+ if(changed) {
+ notifyDataSetChanged();
+ }
+ }
+
+ public void addToList(String pkgName, PackageStats ps) {
+ if(pkgName == null) {
+ Log.w(TAG, "Adding null pkg to List Adapter");
+ return;
+ }
+ ApplicationInfo info;
try {
- appInfo = mPm.getApplicationInfo(ps.packageName, 0);
+ 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;
+ }
+ // 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) {
+ Log.i(TAG, "Strange. Package:"+pkgName+" is not new");
return;
}
- appName = appInfo.loadLabel(mPm);
- appIcon = appInfo.loadIcon(mPm);
+ // New entry
+ newIdx = -newIdx-1;
+ mAppLocalList.add(newIdx, info);
+ mAppPropMap.put(info.packageName, new AppInfo(pkgName, newIdx,
+ info.loadLabel(mPm), info.loadIcon(mPm), ps));
+ adjustIndex();
+ notifyDataSetChanged();
}
+ public void removeFromList(List<String> pkgNames) {
+ if(pkgNames == null) {
+ Log.w(TAG, "Removing null pkg list from List Adapter");
+ return;
+ }
+ int imax = mAppLocalList.size();
+ boolean found = false;
+ ApplicationInfo info;
+ int i, k;
+ String pkgName;
+ int kmax = pkgNames.size();
+ if(kmax <= 0) {
+ Log.w(TAG, "Removing empty pkg list from List Adapter");
+ return;
+ }
+ int idxArr[] = new int[kmax];
+ for (k = 0; k < kmax; k++) {
+ idxArr[k] = -1;
+ }
+ for (i = 0; i < imax; i++) {
+ info = mAppLocalList.get(i);
+ for (k = 0; k < kmax; k++) {
+ pkgName = pkgNames.get(k);
+ if (info.packageName.equalsIgnoreCase(pkgName)) {
+ idxArr[k] = i;
+ found = true;
+ break;
+ }
+ }
+ }
+ // Sort idxArr
+ Arrays.sort(idxArr);
+ // remove the packages based on decending indices
+ for (k = kmax-1; k >= 0; k--) {
+ // Check if package has been found in the list of existing apps first
+ if(idxArr[k] == -1) {
+ break;
+ }
+ info = mAppLocalList.get(idxArr[k]);
+ mAppLocalList.remove(idxArr[k]);
+ mAppPropMap.remove(info.packageName);
+ if (localLOGV) Log.i(TAG, "Removed pkg:"+info.packageName+ " list");
+ }
+ if (found) {
+ adjustIndex();
+ notifyDataSetChanged();
+ }
+ }
+
+ public void updateAppSize(String pkgName, PackageStats ps) {
+ if(pkgName == null) {
+ return;
+ }
+ AppInfo entry = mAppPropMap.get(pkgName);
+ if (entry == null) {
+ Log.w(TAG, "Entry for package:"+pkgName+"doesnt exist in map");
+ return;
+ }
+ // Copy the index into the newly updated entry
+ entry.setSize(ps);
+ notifyDataSetChanged();
+ }
+
+ public PackageStats getAppStats(String pkgName) {
+ if(pkgName == null) {
+ return null;
+ }
+ AppInfo entry = mAppPropMap.get(pkgName);
+ if (entry == null) {
+ return null;
+ }
+ return entry.appStats;
+ }
+ }
+
+ /*
+ * 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(COMPUTE_PKG_SIZE_START);
+ mHandler.removeMessages(COMPUTE_PKG_SIZE_DONE);
+ mHandler.removeMessages(REMOVE_PKG);
+ mHandler.removeMessages(REORDER_LIST);
+ mHandler.removeMessages(ADD_PKG_START);
+ mHandler.removeMessages(ADD_PKG_DONE);
+ }
+
+ 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 {
+ private ApplicationInfo mAppInfo;
+ private int mMsgId;
public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) {
- Bundle data = new Bundle();
- ps = pStats;
- succeeded = pSucceeded;
- if(mChained) {
- data.putInt(ATTR_APP_IDX, mIdx);
- if(succeeded) {
- mIdx++;
+ if(DEBUG_PKG_DELAY) {
+ try {
+ Thread.sleep(10*1000);
+ } catch (InterruptedException e) {
}
}
- data.putBoolean(ATTR_CHAINED, mChained);
- getAppDetails();
- if(localLOGV) Log.i(TAG, "onGetStatsCompleted::"+appInfo.packageName+", ("+ps.cacheSize+","+
- ps.codeSize+", "+ps.dataSize);
- sendMessageToHandler(COMPUTE_PKG_SIZE_DONE, data, 0);
+ AppInfo appInfo = null;
+ Bundle data = new Bundle();
+ data.putString(ATTR_PKG_NAME, mAppInfo.packageName);
+ if(pSucceeded && pStats != null) {
+ if (localLOGV) Log.i(TAG, "onGetStatsCompleted::"+pStats.packageName+", ("+
+ pStats.cacheSize+","+
+ pStats.codeSize+", "+pStats.dataSize);
+ data.putParcelable(ATTR_APP_PKG_STATS, pStats);
+ } else {
+ Log.w(TAG, "Invalid package stats from PackageManager");
+ }
+ //post message to Handler
+ Message msg = mHandler.obtainMessage(mMsgId, data);
+ msg.setData(data);
+ mHandler.sendMessage(msg);
}
-
- public void invokeGetSizeInfo(String packageName, boolean chained) {
- mChained = chained;
- mPm.getPackageSizeInfo(packageName, this);
+
+ public void invokeGetSizeInfo(ApplicationInfo pAppInfo, int msgId) {
+ if(pAppInfo == null || pAppInfo.packageName == null) {
+ return;
+ }
+ if(localLOGV) Log.i(TAG, "Invoking getPackageSizeInfo for package:"+
+ pAppInfo.packageName);
+ mMsgId = msgId;
+ mAppInfo = pAppInfo;
+ mPm.getPackageSizeInfo(pAppInfo.packageName, this);
}
}
@@ -360,239 +998,147 @@ public class ManageApplications extends Activity implements SimpleAdapter.ViewBi
String actionStr = intent.getAction();
Uri data = intent.getData();
String pkgName = data.getEncodedSchemeSpecificPart();
- if(localLOGV) Log.i(TAG, "action:"+actionStr+", for package:"+pkgName);
+ if (localLOGV) Log.i(TAG, "action:"+actionStr+", for package:"+pkgName);
updatePackageList(actionStr, pkgName);
}
}
private void updatePackageList(String actionStr, String pkgName) {
- //technically we dont have to invoke handler since onReceive is invoked on
- //the main thread but doing it here for better clarity
- if(Intent.ACTION_PACKAGE_ADDED.equalsIgnoreCase(actionStr)) {
+ // technically we dont have to invoke handler since onReceive is invoked on
+ // the main thread but doing it here for better clarity
+ if (Intent.ACTION_PACKAGE_ADDED.equalsIgnoreCase(actionStr)) {
Bundle data = new Bundle();
data.putString(ATTR_PKG_NAME, pkgName);
- sendMessageToHandler(ADD_PKG, data, 0);
- } else if(Intent.ACTION_PACKAGE_REMOVED.equalsIgnoreCase(actionStr)) {
+ 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, 0);
- } else if(Intent.ACTION_PACKAGE_CHANGED.equalsIgnoreCase(actionStr)) {
- //force adapter to draw the list again. TODO derive from SimpleAdapter
- //to avoid this
-
- }
- }
-
- /*
- * Utility method to create an array of map objects from a map of map objects
- * for displaying list items to be used in SimpleAdapter.
- */
- private void createListFromValues() {
- findViewById(R.id.center_text).setVisibility(View.GONE);
- populateAdapterList();
- mAppAdapter.setViewBinder(this);
- ListView lv= (ListView) findViewById(android.R.id.list);
- lv.setOnItemClickListener(this);
- lv.setAdapter(mAppAdapter);
- if(mCurrentPkgIdx != -1) {
- lv.setSelection(mCurrentPkgIdx);
+ sendMessageToHandler(REMOVE_PKG, data);
}
}
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- String action = getIntent().getAction();
- if(action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
+ Intent lIntent = getIntent();
+ String action = lIntent.getAction();
+ if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
mSortOrder = SORT_ORDER_SIZE;
}
mPm = getPackageManager();
- //load strings from resources
- mBStr = getString(R.string.b_text);
- mKbStr = getString(R.string.kb_text);
- mMbStr = getString(R.string.mb_text);
+ // initialize some window features
+ requestWindowFeature(Window.FEATURE_RIGHT_ICON);
+ requestWindowFeature(Window.FEATURE_PROGRESS);
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ // init mLoadingDlg
+ mLoadingDlg = new ProgressDialog(this);
+ mLoadingDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ mLoadingDlg.setMessage(getText(R.string.loading));
+ mLoadingDlg.setIndeterminate(true);
+ mLoadingDlg.setOnCancelListener(this);
+ 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);
+ }
+
+ private void showLoadingMsg() {
+ if (mLoadingDlg != null) {
+ if(localLOGV) Log.i(TAG, "Displaying Loading message");
+ mLoadingDlg.show();
+ }
+ }
+
+ private void dismissLoadingMsg() {
+ if ((mLoadingDlg != null) && (mLoadingDlg.isShowing())) {
+ if(localLOGV) Log.i(TAG, "Dismissing Loading message");
+ mLoadingDlg.dismiss();
+ }
}
@Override
public void onStart() {
super.onStart();
setContentView(R.layout.compute_sizes);
- //clear all messages related to application list
- clearMessages();
+ showLoadingMsg();
+ // Create a thread to load resources
+ mResourceThread = new ResourceLoaderThread();
sendMessageToHandler(COMPUTE_PKG_SIZE_START);
}
@Override
public void onStop() {
super.onStop();
- //register receiver here
+ // clear all messages related to application list
+ clearMessagesInHandler();
+ // register receiver here
unregisterReceiver(mReceiver);
+ mAppPropCache = mAppInfoAdapter.mAppPropMap;
}
+ /*
+ * comparator class used to sort AppInfo objects based on size
+ */
public static class AppInfoComparator implements Comparator<ApplicationInfo> {
- public AppInfoComparator(HashMap<String, PackageStats> pSizeMap) {
- mSizeMap= pSizeMap;
+ public AppInfoComparator(Map<String, AppInfo> pAppPropMap) {
+ mAppPropMap= pAppPropMap;
}
public final int compare(ApplicationInfo a, ApplicationInfo b) {
- PackageStats aps, bps;
- aps = mSizeMap.get(a.packageName);
- bps = mSizeMap.get(b.packageName);
- if (aps == null && bps == null) {
- return 0;
- } else if (aps == null) {
+ AppInfo ainfo = mAppPropMap.get(a.packageName);
+ AppInfo binfo = mAppPropMap.get(b.packageName);
+ long atotal = ainfo.getTotalSize();
+ long btotal = binfo.getTotalSize();
+ long ret = atotal - btotal;
+ // negate result to sort in descending order
+ if (ret < 0) {
return 1;
- } else if (bps == null) {
- return -1;
}
- long atotal = aps.dataSize+aps.codeSize+aps.cacheSize;
- long btotal = bps.dataSize+bps.codeSize+bps.cacheSize;
- long ret = atotal-btotal;
- //negate result to sort in descending order
- if(ret < 0) {
- return 1;
- }
- if(ret == 0) {
+ if (ret == 0) {
return 0;
}
return -1;
}
- private HashMap<String, PackageStats> mSizeMap;
+ private Map<String, AppInfo> mAppPropMap;
}
-
- /*
- * Have to extract elements form map and populate a list ot be used by
- * SimpleAdapter when displaying list elements. The sort order has to follow
- * the order of elements in mAppList.
- */
- private List<Map<String, ?>> createAdapterListFromMap() {
- //get the index from mAppInfo which gives the correct sort position
- int imax = mAppList.size();
- if(localLOGV) Log.i(TAG, "Creating new adapter list");
- List<Map<String, ?>> adapterList = new ArrayList<Map<String, ?>>();
- ApplicationInfo tmpInfo;
- for(int i = 0; i < imax; i++) {
- tmpInfo = mAppList.get(i);
- Map<String, Object>newObj = new TreeMap<String, Object>(
- mAppAdapterMap.get(tmpInfo.packageName));
- adapterList.add(newObj);
- }
- return adapterList;
- }
- private void populateAdapterList() {
- mAppAdapter = new SimpleAdapter(this, createAdapterListFromMap(),
- R.layout.manage_applications_item, sKeys, sResourceIds);
- }
-
- private String getSizeStr(PackageStats ps) {
- String retStr = "";
- //insert total size information into map to display in view
- //at this point its guaranteed that ps is not null. but checking anyway
- if(ps != null) {
- long size = ps.cacheSize+ps.codeSize+ps.dataSize;
- if(size < 1024) {
- return String.valueOf(size)+mBStr;
- }
- long kb, mb, rem;
- kb = size >> 10;
- rem = size - (kb << 10);
- if(kb < 1024) {
- if(rem > 512) {
- kb++;
- }
- retStr += String.valueOf(kb)+mKbStr;
- return retStr;
- }
- mb = kb >> 10;
- if(kb >= 512) {
- //round off
- mb++;
- }
- retStr += String.valueOf(mb)+ mMbStr;
- return retStr;
- } else {
- Log.w(TAG, "Something fishy, cannot find size info for package:"+ps.packageName);
- }
- return retStr;
- }
-
- public void sortAppList() {
- // Sort application list
- if(mSortOrder == SORT_ORDER_ALPHA) {
- Collections.sort(mAppList, new ApplicationInfo.DisplayNameComparator(mPm));
- } else if(mSortOrder == SORT_ORDER_SIZE) {
- Collections.sort(mAppList, new AppInfoComparator(mSizeMap));
- }
- }
-
- private Map<String, Object> createMapEntry(CharSequence appName,
- Drawable appIcon, CharSequence appDesc, String sizeStr) {
- Map<String, Object> map = new TreeMap<String, Object>();
- map.put(KEY_APP_NAME, appName);
- //the icon cannot be null. if the application hasnt set it, the default icon is returned.
- map.put(KEY_APP_ICON, appIcon);
- if(appDesc == null) {
- appDesc="";
- }
- map.put(KEY_APP_DESC, appDesc);
- map.put(KEY_APP_SIZE, sizeStr);
- return map;
- }
-
+ // utility method used to start sub activity
private void startApplicationDetailsActivity(ApplicationInfo info, PackageStats ps) {
- //Create intent to start new activity
+ // Create intent to start new activity
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClass(this, InstalledAppDetails.class);
- intent.putExtra(APP_PKG_NAME, info.packageName);
- if(localLOGV) Log.i(TAG, "code="+ps.codeSize+", cache="+ps.cacheSize+", data="+ps.dataSize);
- intent.putExtra(APP_PKG_SIZE, ps);
- if(localLOGV) Log.i(TAG, "Starting sub activity to display info for app:"+info
- +" with intent:"+intent);
- //start new activity to display extended information
- if ((info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
- }
+ mCurrentPkgName = info.packageName;
+ intent.putExtra(APP_PKG_NAME, mCurrentPkgName);
+ intent.putExtra(APP_PKG_SIZE, ps);
+ // start new activity to display extended information
startActivityForResult(intent, INSTALLED_APP_DETAILS);
}
- public boolean setViewValue(View view, Object data, String textRepresentation) {
- if(data == null) {
- return false;
- }
- int id = view.getId();
- switch(id) {
- case R.id.app_name:
- ((TextView)view).setText((String)data);
- break;
- case R.id.app_icon:
- ((ImageView)view).setImageDrawable((Drawable)data);
- break;
- case R.id.app_description:
- ((TextView)view).setText((String)data);
- break;
- case R.id.app_size:
- ((TextView)view).setText((String)data);
- break;
- default:
- break;
- }
- return true;
- }
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, SORT_ORDER_ALPHA, 0, R.string.sort_order_alpha)
+ menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha)
.setIcon(android.R.drawable.ic_menu_sort_alphabetically);
- menu.add(0, SORT_ORDER_SIZE, 0, R.string.sort_order_size)
- .setIcon(android.R.drawable.ic_menu_sort_by_size);
+ menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
+ .setIcon(android.R.drawable.ic_menu_sort_by_size);
+ menu.add(0, FILTER_APPS_ALL, 3, R.string.filter_apps_all);
+ menu.add(0, FILTER_APPS_RUNNING, 4, R.string.filter_apps_running);
+ menu.add(0, FILTER_APPS_THIRD_PARTY, 5, R.string.filter_apps_third_party);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- if(mDoneIniting) {
+ if (mDoneIniting) {
menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
- menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder!= SORT_ORDER_SIZE);
+ menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
+ menu.findItem(FILTER_APPS_ALL).setVisible(mFilterApps != FILTER_APPS_ALL);
+ menu.findItem(FILTER_APPS_THIRD_PARTY).setVisible(
+ mFilterApps != FILTER_APPS_THIRD_PARTY);
+ menu.findItem(FILTER_APPS_RUNNING).setVisible(
+ mFilterApps != FILTER_APPS_RUNNING);
return true;
}
return false;
@@ -607,10 +1153,13 @@ public class ManageApplications extends Activity implements SimpleAdapter.ViewBi
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
- mCurrentPkgIdx=position;
- ApplicationInfo info = mAppList.get(position);
- mCurrentPkg = info;
- PackageStats ps = mSizeMap.get(info.packageName);
- startApplicationDetailsActivity(info, ps);
+ ApplicationInfo info = (ApplicationInfo)mAppInfoAdapter.getItem(position);
+ startApplicationDetailsActivity(info, mAppInfoAdapter.getAppStats(info.packageName));
+ }
+
+ // onCancel call back for dialog thats displayed when data is being loaded
+ public void onCancel(DialogInterface dialog) {
+ mLoadingDlg = null;
+ finish();
}
}