diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:06:01 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:06:01 -0800 |
commit | abc48f80d8747b4fc051b7dd364355ee667a9bac (patch) | |
tree | 31ae577fe29d75963b071e738703e4db83ad6580 /src/com/android/settings/ManageApplications.java | |
parent | de2d9f5f109265873196f1615e1f3546b114aaa7 (diff) | |
download | packages_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.java | 1321 |
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(); } } |