blob: f1550f9780fa650439a7863f3b17f8e619441950 [file] [log] [blame]
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
19import com.android.settings.R;
20import android.app.Activity;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080021import android.app.ActivityManager;
22import android.app.ListActivity;
23import android.app.ProgressDialog;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070024import android.content.BroadcastReceiver;
25import android.content.Context;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080026import android.content.DialogInterface;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070027import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.ApplicationInfo;
30import android.content.pm.IPackageStatsObserver;
31import android.content.pm.PackageManager;
32import android.content.pm.PackageStats;
33import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080034import android.content.res.Resources;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070035import android.graphics.drawable.Drawable;
36import android.net.Uri;
37import android.os.Bundle;
38import android.os.Handler;
39import android.os.Message;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080040import android.text.format.Formatter;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070041import android.util.Config;
42import android.util.Log;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080043import android.view.LayoutInflater;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070044import android.view.Menu;
45import android.view.MenuItem;
46import android.view.View;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080047import android.view.ViewGroup;
48import android.view.Window;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070049import android.widget.AdapterView;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080050import android.widget.BaseAdapter;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070051import android.widget.ImageView;
52import android.widget.ListView;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070053import android.widget.TextView;
54import android.widget.AdapterView.OnItemClickListener;
55import java.util.ArrayList;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080056import java.util.Arrays;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070057import java.util.Collections;
58import java.util.Comparator;
59import java.util.HashMap;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080060import java.util.Iterator;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070061import java.util.List;
62import java.util.Map;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080063import java.util.Set;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070064import java.util.TreeMap;
65
66/**
67 * Activity to pick an application that will be used to display installation information and
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080068 * options to uninstall/delete user data for system applications. This activity
69 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
70 * intent.
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070071 * Initially a compute in progress message is displayed while the application retrieves
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080072 * the list of application information from the PackageManager. The size information
73 * for each package is refreshed to the screen. The resource(app description and
74 * icon) information for each package is not available yet, so some default values for size
75 * icon and descriptions are used initially. Later the resource information for each
76 * application is retrieved and dynamically updated on the screen.
77 * A Broadcast receiver registers for package additions or deletions when the activity is
78 * in focus. If the user installs or deletes packages when the activity has focus, the receiver
79 * gets notified and proceeds to add/delete these packages from the list on the screen.
80 * This is an unlikely scenario but could happen. The entire list gets created every time
81 * the activity's onStart gets invoked. This is to avoid having the receiver for the entire
82 * life cycle of the application.
83 * The applications can be sorted either alphabetically or
84 * based on size(descending). If this activity gets launched under low memory
85 * situations(A low memory notification dispatches intent
86 * ACTION_MANAGE_PACKAGE_STORAGE) the list is sorted per size.
87 * If the user selects an application, extended info(like size, uninstall/clear data options,
88 * permissions info etc.,) is displayed via the InstalledAppDetails activity.
89 * This activity passes the package name and size information to the
90 * InstalledAppDetailsActivity to avoid recomputation of the package size information.
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070091 */
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080092public class ManageApplications extends ListActivity implements
93 OnItemClickListener, DialogInterface.OnCancelListener {
94 // TAG for this activity
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070095 private static final String TAG = "ManageApplications";
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070096
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080097 // log information boolean
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070098 private boolean localLOGV = Config.LOGV || false;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080099
100 // attributes used as keys when passing values to InstalledAppDetails activity
101 public static final String APP_PKG_PREFIX = "com.android.settings.";
102 public static final String APP_PKG_NAME = APP_PKG_PREFIX+"ApplicationPkgName";
103 public static final String APP_PKG_SIZE = APP_PKG_PREFIX+"size";
104 public static final String APP_CHG = APP_PKG_PREFIX+"changed";
105
106 // attribute name used in receiver for tagging names of added/deleted packages
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700107 private static final String ATTR_PKG_NAME="PackageName";
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800108 private static final String ATTR_APP_PKG_STATS="ApplicationPackageStats";
109
110 // constant value that can be used to check return code from sub activity.
111 private static final int INSTALLED_APP_DETAILS = 1;
112
113 // sort order that can be changed through the menu can be sorted alphabetically
114 // or size(descending)
115 private static final int MENU_OPTIONS_BASE = 0;
116 public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 0;
117 public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 1;
118 // Filter options used for displayed list of applications
119 public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 2;
120 public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 3;
121 public static final int FILTER_APPS_RUNNING = MENU_OPTIONS_BASE + 4;
122 // sort order
123 private int mSortOrder = SORT_ORDER_ALPHA;
124 // Filter value
125 int mFilterApps = FILTER_APPS_ALL;
126
127 // Custom Adapter used for managing items in the list
128 private AppInfoAdapter mAppInfoAdapter;
129
130 // messages posted to the handler
131 private static final int HANDLER_MESSAGE_BASE = 0;
132 private static final int COMPUTE_PKG_SIZE_START = HANDLER_MESSAGE_BASE+1;
133 private static final int COMPUTE_PKG_SIZE_DONE = HANDLER_MESSAGE_BASE+2;
134 private static final int REMOVE_PKG = HANDLER_MESSAGE_BASE+3;
135 private static final int REORDER_LIST = HANDLER_MESSAGE_BASE+4;
136 private static final int ADD_PKG_START = HANDLER_MESSAGE_BASE+5;
137 private static final int ADD_PKG_DONE = HANDLER_MESSAGE_BASE+6;
138 private static final int REFRESH_ICONS = HANDLER_MESSAGE_BASE+7;
139
140 // observer object used for computing pkg sizes
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700141 private PkgSizeObserver mObserver;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800142 // local handle to PackageManager
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700143 private PackageManager mPm;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800144 // Broadcast Receiver object that receives notifications for added/deleted
145 // packages
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700146 private PackageIntentReceiver mReceiver;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800147 // atomic variable used to track if computing pkg sizes is in progress. should be volatile?
148
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700149 private boolean mDoneIniting = false;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800150 // default icon thats used when displaying applications initially before resource info is
151 // retrieved
152 private Drawable mDefaultAppIcon;
153
154 // temporary dialog displayed while the application info loads
155 private ProgressDialog mLoadingDlg = null;
156
157 // compute index used to track the application size computations
158 private int mComputeIndex;
159
160 // Size resource used for packages whose size computation failed for some reason
161 private CharSequence mInvalidSizeStr;
162 private CharSequence mComputingSizeStr;
163
164 // map used to store list of added and removed packages. Immutable Boolean
165 // variables indicate if a package has been added or removed. If a package is
166 // added or deleted multiple times a single entry with the latest operation will
167 // be recorded in the map.
168 private Map<String, Boolean> mAddRemoveMap;
169
170 // layout inflater object used to inflate views
171 private LayoutInflater mInflater;
172
173 // invalid size value used initially and also when size retrieval through PackageManager
174 // fails for whatever reason
175 private static final int SIZE_INVALID = -1;
176
177 // debug boolean variable to test delays from PackageManager API's
178 private boolean DEBUG_PKG_DELAY = false;
179
180 // Thread to load resources
181 ResourceLoaderThread mResourceThread;
182
183 String mCurrentPkgName;
184
185 //TODO implement a cache system
186 private Map<String, AppInfo> mAppPropCache;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700187
188 /*
189 * Handler class to handle messages for various operations
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800190 * Most of the operations that effect Application related data
191 * are posted as messages to the handler to avoid synchronization
192 * when accessing these structures.
193 * When the size retrieval gets kicked off for the first time, a COMPUTE_PKG_SIZE_START
194 * message is posted to the handler which invokes the getSizeInfo for the pkg at index 0
195 * When the PackageManager's asynchronous call back through
196 * PkgSizeObserver.onGetStatsCompleted gets invoked, the application resources like
197 * label, description, icon etc., is loaded in the same thread and these values are
198 * set on the observer. The observer then posts a COMPUTE_PKG_SIZE_DONE message
199 * to the handler. This information is updated on the AppInfoAdapter associated with
200 * the list view of this activity and size info retrieval is initiated for the next package as
201 * indicated by mComputeIndex
202 * When a package gets added while the activity has focus, the PkgSizeObserver posts
203 * ADD_PKG_START message to the handler. If the computation is not in progress, the size
204 * is retrieved for the newly added package through the observer object and the newly
205 * installed app info is updated on the screen. If the computation is still in progress
206 * the package is added to an internal structure and action deferred till the computation
207 * is done for all the packages.
208 * When a package gets deleted, REMOVE_PKG is posted to the handler
209 * if computation is not in progress(as indicated by
210 * mDoneIniting), the package is deleted from the displayed list of apps. If computation is
211 * still in progress the package is added to an internal structure and action deferred till
212 * the computation is done for all packages.
213 * When the sizes of all packages is computed, the newly
214 * added or removed packages are processed in order.
215 * If the user changes the order in which these applications are viewed by hitting the
216 * menu key, REORDER_LIST message is posted to the handler. this sorts the list
217 * of items based on the sort order.
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700218 */
219 private Handler mHandler = new Handler() {
220 public void handleMessage(Message msg) {
221 PackageStats ps;
222 ApplicationInfo info;
223 Bundle data;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800224 String pkgName = null;
225 AppInfo appInfo;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700226 data = msg.getData();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800227 if(data != null) {
228 pkgName = data.getString(ATTR_PKG_NAME);
229 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700230 switch (msg.what) {
231 case COMPUTE_PKG_SIZE_START:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800232 if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_START");
233 setProgressBarIndeterminateVisibility(true);
234 mComputeIndex = 0;
235 initAppList(mFilterApps);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700236 break;
237 case COMPUTE_PKG_SIZE_DONE:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800238 if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
239 if(pkgName == null) {
240 Log.w(TAG, "Ignoring message");
241 break;
242 }
243 ps = data.getParcelable(ATTR_APP_PKG_STATS);
244 if(ps == null) {
245 Log.i(TAG, "Invalid package stats for package:"+pkgName);
246 } else {
247 int pkgId = mAppInfoAdapter.getIndex(pkgName);
248 if(mComputeIndex != pkgId) {
249 //spurious call from stale observer
250 Log.w(TAG, "Stale call back from PkgSizeObserver");
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700251 break;
252 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800253 mAppInfoAdapter.updateAppSize(pkgName, ps);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700254 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800255 mComputeIndex++;
256 if (mComputeIndex < mAppInfoAdapter.getCount()) {
257 // initiate compute package size for next pkg in list
258 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
259 mComputeIndex),
260 COMPUTE_PKG_SIZE_DONE);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700261 } else {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800262 // check for added/removed packages
263 Set<String> keys = mAddRemoveMap.keySet();
264 Iterator<String> iter = keys.iterator();
265 List<String> removeList = new ArrayList<String>();
266 boolean added = false;
267 boolean removed = false;
268 while (iter.hasNext()) {
269 String key = iter.next();
270 if (mAddRemoveMap.get(key) == Boolean.TRUE) {
271 // add
272 try {
273 info = mPm.getApplicationInfo(key, 0);
274 mAppInfoAdapter.addApplicationInfo(info);
275 added = true;
276 } catch (NameNotFoundException e) {
277 Log.w(TAG, "Invalid added package:"+key+" Ignoring entry");
278 }
279 } else {
280 // remove
281 removeList.add(key);
282 removed = true;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700283 }
284 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800285 // remove uninstalled packages from list
286 if (removed) {
287 mAppInfoAdapter.removeFromList(removeList);
288 }
289 // handle newly installed packages
290 if (added) {
291 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
292 mComputeIndex),
293 COMPUTE_PKG_SIZE_DONE);
294 } else {
295 // end computation here
296 mDoneIniting = true;
297 mAppInfoAdapter.sortList(mSortOrder);
298 //load resources now
299 if(mResourceThread.isAlive()) {
300 mResourceThread.interrupt();
301 }
302 mResourceThread.loadAllResources(mAppInfoAdapter.getAppList());
303 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700304 }
305 break;
306 case REMOVE_PKG:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800307 if(localLOGV) Log.i(TAG, "Message REMOVE_PKG");
308 if(pkgName == null) {
309 Log.w(TAG, "Ignoring message:REMOVE_PKG for null pkgName");
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700310 break;
311 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800312 if (!mDoneIniting) {
313 Boolean currB = mAddRemoveMap.get(pkgName);
314 if (currB == null || (currB.equals(Boolean.TRUE))) {
315 mAddRemoveMap.put(pkgName, Boolean.FALSE);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700316 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800317 break;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700318 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800319 List<String> pkgList = new ArrayList<String>();
320 pkgList.add(pkgName);
321 mAppInfoAdapter.removeFromList(pkgList);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700322 break;
323 case REORDER_LIST:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800324 if(localLOGV) Log.i(TAG, "Message REORDER_LIST");
325 int menuOption = msg.arg1;
326 if((menuOption == SORT_ORDER_ALPHA) ||
327 (menuOption == SORT_ORDER_SIZE)) {
328 // Option to sort list
329 if (menuOption != mSortOrder) {
330 mSortOrder = menuOption;
331 if (localLOGV) Log.i(TAG, "Changing sort order to "+mSortOrder);
332 mAppInfoAdapter.sortList(mSortOrder);
333 }
334 } else if(menuOption != mFilterApps) {
335 // Option to filter list
336 mFilterApps = menuOption;
337 boolean ret = mAppInfoAdapter.resetAppList(mFilterApps,
338 getInstalledApps(mFilterApps));
339 if(!ret) {
340 // Reset cache
341 mAppPropCache = null;
342 mFilterApps = FILTER_APPS_ALL;
343 mHandler.sendEmptyMessage(COMPUTE_PKG_SIZE_START);
344 sendMessageToHandler(REORDER_LIST, menuOption);
345 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700346 }
347 break;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800348 case ADD_PKG_START:
349 if(localLOGV) Log.i(TAG, "Message ADD_PKG_START");
350 if(pkgName == null) {
351 Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700352 break;
353 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800354 if (!mDoneIniting) {
355 Boolean currB = mAddRemoveMap.get(pkgName);
356 if (currB == null || (currB.equals(Boolean.FALSE))) {
357 mAddRemoveMap.put(pkgName, Boolean.TRUE);
358 }
359 break;
360 }
361 try {
362 info = mPm.getApplicationInfo(pkgName, 0);
363 } catch (NameNotFoundException e) {
364 Log.w(TAG, "Couldnt find application info for:"+pkgName);
365 break;
366 }
367 mObserver.invokeGetSizeInfo(info, ADD_PKG_DONE);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700368 break;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800369 case ADD_PKG_DONE:
370 if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
371 if(pkgName == null) {
372 Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
373 break;
374 }
375 ps = data.getParcelable(ATTR_APP_PKG_STATS);
376 mAppInfoAdapter.addToList(pkgName, ps);
377 break;
378 case REFRESH_ICONS:
379 Map<String, AppInfo> iconMap = (Map<String, AppInfo>) msg.obj;
380 if(iconMap == null) {
381 Log.w(TAG, "Error loading icons for applications");
382 } else {
383 mAppInfoAdapter.updateAppsResourceInfo(iconMap);
384 setProgressBarIndeterminateVisibility(false);
385 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700386 default:
387 break;
388 }
389 }
390 };
391
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800392 List<ApplicationInfo> getInstalledApps(int filterOption) {
393 List<ApplicationInfo> installedAppList = mPm.getInstalledApplications(
394 PackageManager.GET_UNINSTALLED_PACKAGES);
395 if (installedAppList == null) {
396 return new ArrayList<ApplicationInfo> ();
397 }
398 if (filterOption == FILTER_APPS_THIRD_PARTY) {
399 List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
400 for (ApplicationInfo appInfo : installedAppList) {
401 if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
402 appList.add(appInfo);
403 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700404 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800405 return appList;
406 } else if (filterOption == FILTER_APPS_RUNNING) {
407 List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
408 List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
409 if ((procList == null) || (procList.size() == 0)) {
410 return appList;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700411 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800412 // Retrieve running processes from ActivityManager
413 for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
414 if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
415 int size = appProcInfo.pkgList.length;
416 for (int i = 0; i < size; i++) {
417 ApplicationInfo appInfo = null;
418 try {
419 appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i],
420 PackageManager.GET_UNINSTALLED_PACKAGES);
421 } catch (NameNotFoundException e) {
422 Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]);
423 continue;
424 }
425 if(appInfo != null) {
426 appList.add(appInfo);
427 }
428 }
429 }
430 }
431 return appList;
432 } else {
433 return installedAppList;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700434 }
435 }
436
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800437 private List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesList() {
438 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
439 return am.getRunningAppProcesses();
440 }
441
442 // some initialization code used when kicking off the size computation
443 private void initAppList(int filterOption) {
444 mDoneIniting = false;
445 // Initialize lists
446 List<ApplicationInfo> appList = getInstalledApps(filterOption);
447 mAddRemoveMap = new TreeMap<String, Boolean>();
448 mAppInfoAdapter = new AppInfoAdapter(this, appList);
449 dismissLoadingMsg();
450 // get list and set listeners and adapter
451 ListView lv= (ListView) findViewById(android.R.id.list);
452 lv.setOnItemClickListener(this);
453 lv.setSaveEnabled(true);
454 lv.setItemsCanFocus(true);
455 lv.setOnItemClickListener(this);
456 lv.setAdapter(mAppInfoAdapter);
457 // register receiver
458 mReceiver = new PackageIntentReceiver();
459 mReceiver.registerReceiver();
460 // initiate compute pkg sizes
461 if (localLOGV) Log.i(TAG, "Initiating compute sizes for first time");
462 mObserver = new PkgSizeObserver();
463 if(appList.size() > 0) {
464 mObserver.invokeGetSizeInfo(appList.get(0), COMPUTE_PKG_SIZE_DONE);
465 } else {
466 mDoneIniting = true;
467 }
468 }
469
470 // internal structure used to track added and deleted packages when
471 // the activity has focus
472 class AddRemoveInfo {
473 String pkgName;
474 boolean add;
475 public AddRemoveInfo(String pPkgName, boolean pAdd) {
476 pkgName = pPkgName;
477 add = pAdd;
478 }
479 }
480
481 class ResourceLoaderThread extends Thread {
482 List<ApplicationInfo> mAppList;
483
484 void loadAllResources(List<ApplicationInfo> appList) {
485 if(appList == null || appList.size() <= 0) {
486 Log.w(TAG, "Empty or null application list");
487 return;
488 }
489 mAppList = appList;
490 start();
491 }
492
493 public void run() {
494 Map<String, AppInfo> iconMap = new HashMap<String, AppInfo>();
495 for (ApplicationInfo appInfo : mAppList) {
496 CharSequence appName = appInfo.loadLabel(mPm);
497 Drawable appIcon = appInfo.loadIcon(mPm);
498 iconMap.put(appInfo.packageName,
499 new AppInfo(appInfo.packageName, appName, appIcon));
500 }
501 Message msg = mHandler.obtainMessage(REFRESH_ICONS);
502 msg.obj = iconMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700503 mHandler.sendMessage(msg);
504 }
505 }
506
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800507 /* Internal class representing an application or packages displayable attributes
508 *
509 */
510 class AppInfo {
511 public String pkgName;
512 int index;
513 public CharSequence appName;
514 public Drawable appIcon;
515 public CharSequence appSize;
516 public PackageStats appStats;
517
518 public void refreshIcon(AppInfo pInfo) {
519 appName = pInfo.appName;
520 appIcon = pInfo.appIcon;
521 }
522
523 public AppInfo(String pName, CharSequence aName, Drawable aIcon) {
524 index = -1;
525 pkgName = pName;
526 appName = aName;
527 appIcon = aIcon;
528 appStats = null;
529 appSize = mComputingSizeStr;
530 }
531
532 public AppInfo(String pName, int pIndex, CharSequence aName, Drawable aIcon,
533 PackageStats ps) {
534 index = pIndex;
535 pkgName = pName;
536 appName = aName;
537 appIcon = aIcon;
538 if(ps == null) {
539 appSize = mComputingSizeStr;
540 } else {
541 appStats = ps;
542 appSize = getSizeStr();
543 }
544 }
545 public void setSize(PackageStats ps) {
546 appStats = ps;
547 if (ps != null) {
548 appSize = getSizeStr();
549 }
550 }
551 public long getTotalSize() {
552 PackageStats ps = appStats;
553 if (ps != null) {
554 return ps.cacheSize+ps.codeSize+ps.dataSize;
555 }
556 return SIZE_INVALID;
557 }
558
559 private String getSizeStr() {
560 PackageStats ps = appStats;
561 String retStr = "";
562 // insert total size information into map to display in view
563 // at this point its guaranteed that ps is not null. but checking anyway
564 if (ps != null) {
565 long size = getTotalSize();
566 if (size == SIZE_INVALID) {
567 return mInvalidSizeStr.toString();
568 }
569 return Formatter.formatFileSize(ManageApplications.this, size);
570 }
571 return retStr;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700572 }
573 }
574
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800575 // View Holder used when displaying views
576 static class AppViewHolder {
577 TextView appName;
578 ImageView appIcon;
579 TextView appSize;
580 }
581
582 /* Custom adapter implementation for the ListView
583 * This adapter maintains a map for each displayed application and its properties
584 * An index value on each AppInfo object indicates the correct position or index
585 * in the list. If the list gets updated dynamically when the user is viewing the list of
586 * applications, we need to return the correct index of position. This is done by mapping
587 * the getId methods via the package name into the internal maps and indices.
588 * The order of applications in the list is mirrored in mAppLocalList
589 */
590 class AppInfoAdapter extends BaseAdapter {
591 private Map<String, AppInfo> mAppPropMap;
592 private List<ApplicationInfo> mAppLocalList;
593 ApplicationInfo.DisplayNameComparator mAlphaComparator;
594 AppInfoComparator mSizeComparator;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700595
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800596 private AppInfo getFromCache(String packageName) {
597 if(mAppPropCache == null) {
598 return null;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700599 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800600 return mAppPropCache.get(packageName);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700601 }
602
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800603 public AppInfoAdapter(Context c, List<ApplicationInfo> appList) {
604 mAppLocalList = appList;
605 boolean useCache = false;
606 int sortOrder = SORT_ORDER_ALPHA;
607 int imax = mAppLocalList.size();
608 if(mAppPropCache != null) {
609 useCache = true;
610 // Activity has been resumed. can use the cache to populate values initially
611 mAppPropMap = mAppPropCache;
612 sortOrder = mSortOrder;
613 }
614 sortAppList(sortOrder);
615 // Recreate property map
616 mAppPropMap = new TreeMap<String, AppInfo>();
617 for (int i = 0; i < imax; i++) {
618 ApplicationInfo info = mAppLocalList.get(i);
619 AppInfo aInfo = getFromCache(info.packageName);
620 if(aInfo == null){
621 aInfo = new AppInfo(info.packageName, i,
622 info.packageName, mDefaultAppIcon, null);
623 } else {
624 aInfo.index = i;
625 }
626 mAppPropMap.put(info.packageName, aInfo);
627 }
628 }
629
630 public int getCount() {
631 return mAppLocalList.size();
632 }
633
634 public Object getItem(int position) {
635 return mAppLocalList.get(position);
636 }
637
638 /*
639 * This method returns the index of the package position in the application list
640 */
641 public int getIndex(String pkgName) {
642 if(pkgName == null) {
643 Log.w(TAG, "Getting index of null package in List Adapter");
644 }
645 int imax = mAppLocalList.size();
646 ApplicationInfo appInfo;
647 for(int i = 0; i < imax; i++) {
648 appInfo = mAppLocalList.get(i);
649 if(appInfo.packageName.equalsIgnoreCase(pkgName)) {
650 return i;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700651 }
652 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800653 return -1;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700654 }
655
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800656 public ApplicationInfo getApplicationInfo(int position) {
657 int imax = mAppLocalList.size();
658 if( (position < 0) || (position >= imax)) {
659 Log.w(TAG, "Position out of bounds in List Adapter");
660 return null;
661 }
662 return mAppLocalList.get(position);
663 }
664
665 public void addApplicationInfo(ApplicationInfo info) {
666 if(info == null) {
667 Log.w(TAG, "Ignoring null add in List Adapter");
668 return;
669 }
670 mAppLocalList.add(info);
671 }
672
673 public long getItemId(int position) {
674 int imax = mAppLocalList.size();
675 if( (position < 0) || (position >= imax)) {
676 Log.w(TAG, "Position out of bounds in List Adapter");
677 return -1;
678 }
679 return mAppPropMap.get(mAppLocalList.get(position).packageName).index;
680 }
681
682 public List<ApplicationInfo> getAppList() {
683 return mAppLocalList;
684 }
685
686 public View getView(int position, View convertView, ViewGroup parent) {
687 // A ViewHolder keeps references to children views to avoid unneccessary calls
688 // to findViewById() on each row.
689 AppViewHolder holder;
690
691 // When convertView is not null, we can reuse it directly, there is no need
692 // to reinflate it. We only inflate a new View when the convertView supplied
693 // by ListView is null.
694 if (convertView == null) {
695 convertView = mInflater.inflate(R.layout.manage_applications_item, null);
696
697 // Creates a ViewHolder and store references to the two children views
698 // we want to bind data to.
699 holder = new AppViewHolder();
700 holder.appName = (TextView) convertView.findViewById(R.id.app_name);
701 holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
702 holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
703 convertView.setTag(holder);
704 } else {
705 // Get the ViewHolder back to get fast access to the TextView
706 // and the ImageView.
707 holder = (AppViewHolder) convertView.getTag();
708 }
709
710 // Bind the data efficiently with the holder
711 ApplicationInfo appInfo = mAppLocalList.get(position);
712 AppInfo mInfo = mAppPropMap.get(appInfo.packageName);
713 if(mInfo != null) {
714 if(mInfo.appName != null) {
715 holder.appName.setText(mInfo.appName);
716 }
717 if(mInfo.appIcon != null) {
718 holder.appIcon.setImageDrawable(mInfo.appIcon);
719 }
720 holder.appSize.setText(mInfo.appSize);
721 } else {
722 Log.w(TAG, "No info for package:"+appInfo.packageName+" in property map");
723 }
724 return convertView;
725 }
726
727 private void adjustIndex() {
728 int imax = mAppLocalList.size();
729 ApplicationInfo info;
730 for (int i = 0; i < imax; i++) {
731 info = mAppLocalList.get(i);
732 mAppPropMap.get(info.packageName).index = i;
733 }
734 }
735
736 public void sortAppList(int sortOrder) {
737 Collections.sort(mAppLocalList, getAppComparator(sortOrder));
738 }
739
740 public void sortList(int sortOrder) {
741 sortAppList(sortOrder);
742 adjustIndex();
743 notifyDataSetChanged();
744 }
745
746 public boolean resetAppList(int filterOption, List<ApplicationInfo> appList) {
747 // Create application list based on the filter value
748 mAppLocalList = appList;
749 // Check for all properties in map before sorting. Populate values from cache
750 for(ApplicationInfo applicationInfo : mAppLocalList) {
751 AppInfo appInfo = mAppPropMap.get(applicationInfo.packageName);
752 if(appInfo == null) {
753 AppInfo rInfo = getFromCache(applicationInfo.packageName);
754 if(rInfo == null) {
755 // Need to load resources again. Inconsistency somewhere
756 return false;
757 }
758 mAppPropMap.put(applicationInfo.packageName, rInfo);
759 }
760 }
761 sortList(mSortOrder);
762 return true;
763 }
764
765 private Comparator<ApplicationInfo> getAppComparator(int sortOrder) {
766 if (sortOrder == SORT_ORDER_ALPHA) {
767 // Lazy initialization
768 if (mAlphaComparator == null) {
769 mAlphaComparator = new ApplicationInfo.DisplayNameComparator(mPm);
770 }
771 return mAlphaComparator;
772 }
773 // Lazy initialization
774 if(mSizeComparator == null) {
775 mSizeComparator = new AppInfoComparator(mAppPropMap);
776 }
777 return mSizeComparator;
778 }
779
780 public void updateAppsResourceInfo(Map<String, AppInfo> iconMap) {
781 if(iconMap == null) {
782 Log.w(TAG, "Null iconMap when refreshing icon in List Adapter");
783 return;
784 }
785 boolean changed = false;
786 for (ApplicationInfo info : mAppLocalList) {
787 AppInfo pInfo = iconMap.get(info.packageName);
788 if(pInfo != null) {
789 AppInfo aInfo = mAppPropMap.get(info.packageName);
790 aInfo.refreshIcon(pInfo);
791 changed = true;
792 }
793 }
794 if(changed) {
795 notifyDataSetChanged();
796 }
797 }
798
799 public void addToList(String pkgName, PackageStats ps) {
800 if(pkgName == null) {
801 Log.w(TAG, "Adding null pkg to List Adapter");
802 return;
803 }
804 ApplicationInfo info;
805 try {
806 info = mPm.getApplicationInfo(pkgName, 0);
807 } catch (NameNotFoundException e) {
808 Log.w(TAG, "Ignoring non-existent package:"+pkgName);
809 return;
810 }
811 if(info == null) {
812 // Nothing to do log error message and return
813 Log.i(TAG, "Null ApplicationInfo for package:"+pkgName);
814 return;
815 }
816 // Binary search returns a negative index (ie --index) of the position where
817 // this might be inserted.
818 int newIdx = Collections.binarySearch(mAppLocalList, info,
819 getAppComparator(mSortOrder));
820 if(newIdx >= 0) {
821 Log.i(TAG, "Strange. Package:"+pkgName+" is not new");
822 return;
823 }
824 // New entry
825 newIdx = -newIdx-1;
826 mAppLocalList.add(newIdx, info);
827 mAppPropMap.put(info.packageName, new AppInfo(pkgName, newIdx,
828 info.loadLabel(mPm), info.loadIcon(mPm), ps));
829 adjustIndex();
830 notifyDataSetChanged();
831 }
832
833 public void removeFromList(List<String> pkgNames) {
834 if(pkgNames == null) {
835 Log.w(TAG, "Removing null pkg list from List Adapter");
836 return;
837 }
838 int imax = mAppLocalList.size();
839 boolean found = false;
840 ApplicationInfo info;
841 int i, k;
842 String pkgName;
843 int kmax = pkgNames.size();
844 if(kmax <= 0) {
845 Log.w(TAG, "Removing empty pkg list from List Adapter");
846 return;
847 }
848 int idxArr[] = new int[kmax];
849 for (k = 0; k < kmax; k++) {
850 idxArr[k] = -1;
851 }
852 for (i = 0; i < imax; i++) {
853 info = mAppLocalList.get(i);
854 for (k = 0; k < kmax; k++) {
855 pkgName = pkgNames.get(k);
856 if (info.packageName.equalsIgnoreCase(pkgName)) {
857 idxArr[k] = i;
858 found = true;
859 break;
860 }
861 }
862 }
863 // Sort idxArr
864 Arrays.sort(idxArr);
865 // remove the packages based on decending indices
866 for (k = kmax-1; k >= 0; k--) {
867 // Check if package has been found in the list of existing apps first
868 if(idxArr[k] == -1) {
869 break;
870 }
871 info = mAppLocalList.get(idxArr[k]);
872 mAppLocalList.remove(idxArr[k]);
873 mAppPropMap.remove(info.packageName);
874 if (localLOGV) Log.i(TAG, "Removed pkg:"+info.packageName+ " list");
875 }
876 if (found) {
877 adjustIndex();
878 notifyDataSetChanged();
879 }
880 }
881
882 public void updateAppSize(String pkgName, PackageStats ps) {
883 if(pkgName == null) {
884 return;
885 }
886 AppInfo entry = mAppPropMap.get(pkgName);
887 if (entry == null) {
888 Log.w(TAG, "Entry for package:"+pkgName+"doesnt exist in map");
889 return;
890 }
891 // Copy the index into the newly updated entry
892 entry.setSize(ps);
893 notifyDataSetChanged();
894 }
895
896 public PackageStats getAppStats(String pkgName) {
897 if(pkgName == null) {
898 return null;
899 }
900 AppInfo entry = mAppPropMap.get(pkgName);
901 if (entry == null) {
902 return null;
903 }
904 return entry.appStats;
905 }
906 }
907
908 /*
909 * Utility method to clear messages to Handler
910 * We need'nt synchronize on the Handler since posting messages is guaranteed
911 * to be thread safe. Even if the other thread that retrieves package sizes
912 * posts a message, we do a cursory check of validity on mAppInfoAdapter's applist
913 */
914 private void clearMessagesInHandler() {
915 mHandler.removeMessages(COMPUTE_PKG_SIZE_START);
916 mHandler.removeMessages(COMPUTE_PKG_SIZE_DONE);
917 mHandler.removeMessages(REMOVE_PKG);
918 mHandler.removeMessages(REORDER_LIST);
919 mHandler.removeMessages(ADD_PKG_START);
920 mHandler.removeMessages(ADD_PKG_DONE);
921 }
922
923 private void sendMessageToHandler(int msgId, int arg1) {
924 Message msg = mHandler.obtainMessage(msgId);
925 msg.arg1 = arg1;
926 mHandler.sendMessage(msg);
927 }
928
929 private void sendMessageToHandler(int msgId, Bundle data) {
930 Message msg = mHandler.obtainMessage(msgId);
931 msg.setData(data);
932 mHandler.sendMessage(msg);
933 }
934
935 private void sendMessageToHandler(int msgId) {
936 mHandler.sendEmptyMessage(msgId);
937 }
938
939 /*
940 * Stats Observer class used to compute package sizes and retrieve size information
941 * PkgSizeOberver is the call back thats used when invoking getPackageSizeInfo on
942 * PackageManager. The values in call back onGetStatsCompleted are validated
943 * and the specified message is passed to mHandler. The package name
944 * and the AppInfo object corresponding to the package name are set on the message
945 */
946 class PkgSizeObserver extends IPackageStatsObserver.Stub {
947 private ApplicationInfo mAppInfo;
948 private int mMsgId;
949 public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) {
950 if(DEBUG_PKG_DELAY) {
951 try {
952 Thread.sleep(10*1000);
953 } catch (InterruptedException e) {
954 }
955 }
956 AppInfo appInfo = null;
957 Bundle data = new Bundle();
958 data.putString(ATTR_PKG_NAME, mAppInfo.packageName);
959 if(pSucceeded && pStats != null) {
960 if (localLOGV) Log.i(TAG, "onGetStatsCompleted::"+pStats.packageName+", ("+
961 pStats.cacheSize+","+
962 pStats.codeSize+", "+pStats.dataSize);
963 data.putParcelable(ATTR_APP_PKG_STATS, pStats);
964 } else {
965 Log.w(TAG, "Invalid package stats from PackageManager");
966 }
967 //post message to Handler
968 Message msg = mHandler.obtainMessage(mMsgId, data);
969 msg.setData(data);
970 mHandler.sendMessage(msg);
971 }
972
973 public void invokeGetSizeInfo(ApplicationInfo pAppInfo, int msgId) {
974 if(pAppInfo == null || pAppInfo.packageName == null) {
975 return;
976 }
977 if(localLOGV) Log.i(TAG, "Invoking getPackageSizeInfo for package:"+
978 pAppInfo.packageName);
979 mMsgId = msgId;
980 mAppInfo = pAppInfo;
981 mPm.getPackageSizeInfo(pAppInfo.packageName, this);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700982 }
983 }
984
985 /**
986 * Receives notifications when applications are added/removed.
987 */
988 private class PackageIntentReceiver extends BroadcastReceiver {
989 void registerReceiver() {
990 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
991 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
992 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
993 filter.addDataScheme("package");
994 ManageApplications.this.registerReceiver(this, filter);
995 }
996 @Override
997 public void onReceive(Context context, Intent intent) {
998 String actionStr = intent.getAction();
999 Uri data = intent.getData();
1000 String pkgName = data.getEncodedSchemeSpecificPart();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001001 if (localLOGV) Log.i(TAG, "action:"+actionStr+", for package:"+pkgName);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001002 updatePackageList(actionStr, pkgName);
1003 }
1004 }
1005
1006 private void updatePackageList(String actionStr, String pkgName) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001007 // technically we dont have to invoke handler since onReceive is invoked on
1008 // the main thread but doing it here for better clarity
1009 if (Intent.ACTION_PACKAGE_ADDED.equalsIgnoreCase(actionStr)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001010 Bundle data = new Bundle();
1011 data.putString(ATTR_PKG_NAME, pkgName);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001012 sendMessageToHandler(ADD_PKG_START, data);
1013 } else if (Intent.ACTION_PACKAGE_REMOVED.equalsIgnoreCase(actionStr)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001014 Bundle data = new Bundle();
1015 data.putString(ATTR_PKG_NAME, pkgName);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001016 sendMessageToHandler(REMOVE_PKG, data);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001017 }
1018 }
1019
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001020
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001021 @Override
1022 protected void onCreate(Bundle savedInstanceState) {
1023 super.onCreate(savedInstanceState);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001024 Intent lIntent = getIntent();
1025 String action = lIntent.getAction();
1026 if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001027 mSortOrder = SORT_ORDER_SIZE;
1028 }
1029 mPm = getPackageManager();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001030 // initialize some window features
1031 requestWindowFeature(Window.FEATURE_RIGHT_ICON);
1032 requestWindowFeature(Window.FEATURE_PROGRESS);
1033 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
1034 // init mLoadingDlg
1035 mLoadingDlg = new ProgressDialog(this);
1036 mLoadingDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
1037 mLoadingDlg.setMessage(getText(R.string.loading));
1038 mLoadingDlg.setIndeterminate(true);
1039 mLoadingDlg.setOnCancelListener(this);
1040 mDefaultAppIcon =Resources.getSystem().getDrawable(
1041 com.android.internal.R.drawable.sym_def_app_icon);
1042 mInvalidSizeStr = getText(R.string.invalid_size_value);
1043 mComputingSizeStr = getText(R.string.computing_size);
1044 // initialize the inflater
1045 mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1046 }
1047
1048 private void showLoadingMsg() {
1049 if (mLoadingDlg != null) {
1050 if(localLOGV) Log.i(TAG, "Displaying Loading message");
1051 mLoadingDlg.show();
1052 }
1053 }
1054
1055 private void dismissLoadingMsg() {
1056 if ((mLoadingDlg != null) && (mLoadingDlg.isShowing())) {
1057 if(localLOGV) Log.i(TAG, "Dismissing Loading message");
1058 mLoadingDlg.dismiss();
1059 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001060 }
1061
1062 @Override
1063 public void onStart() {
1064 super.onStart();
1065 setContentView(R.layout.compute_sizes);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001066 showLoadingMsg();
1067 // Create a thread to load resources
1068 mResourceThread = new ResourceLoaderThread();
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001069 sendMessageToHandler(COMPUTE_PKG_SIZE_START);
1070 }
1071
1072 @Override
1073 public void onStop() {
1074 super.onStop();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001075 // clear all messages related to application list
1076 clearMessagesInHandler();
1077 // register receiver here
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001078 unregisterReceiver(mReceiver);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001079 mAppPropCache = mAppInfoAdapter.mAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001080 }
1081
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001082 /*
1083 * comparator class used to sort AppInfo objects based on size
1084 */
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001085 public static class AppInfoComparator implements Comparator<ApplicationInfo> {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001086 public AppInfoComparator(Map<String, AppInfo> pAppPropMap) {
1087 mAppPropMap= pAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001088 }
1089
1090 public final int compare(ApplicationInfo a, ApplicationInfo b) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001091 AppInfo ainfo = mAppPropMap.get(a.packageName);
1092 AppInfo binfo = mAppPropMap.get(b.packageName);
1093 long atotal = ainfo.getTotalSize();
1094 long btotal = binfo.getTotalSize();
1095 long ret = atotal - btotal;
1096 // negate result to sort in descending order
1097 if (ret < 0) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001098 return 1;
1099 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001100 if (ret == 0) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001101 return 0;
1102 }
1103 return -1;
1104 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001105 private Map<String, AppInfo> mAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001106 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001107
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001108 // utility method used to start sub activity
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001109 private void startApplicationDetailsActivity(ApplicationInfo info, PackageStats ps) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001110 // Create intent to start new activity
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001111 Intent intent = new Intent(Intent.ACTION_VIEW);
1112 intent.setClass(this, InstalledAppDetails.class);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001113 mCurrentPkgName = info.packageName;
1114 intent.putExtra(APP_PKG_NAME, mCurrentPkgName);
1115 intent.putExtra(APP_PKG_SIZE, ps);
1116 // start new activity to display extended information
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001117 startActivityForResult(intent, INSTALLED_APP_DETAILS);
1118 }
1119
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001120 @Override
1121 public boolean onCreateOptionsMenu(Menu menu) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001122 menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha)
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001123 .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001124 menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
1125 .setIcon(android.R.drawable.ic_menu_sort_by_size);
1126 menu.add(0, FILTER_APPS_ALL, 3, R.string.filter_apps_all);
1127 menu.add(0, FILTER_APPS_RUNNING, 4, R.string.filter_apps_running);
1128 menu.add(0, FILTER_APPS_THIRD_PARTY, 5, R.string.filter_apps_third_party);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001129 return true;
1130 }
1131
1132 @Override
1133 public boolean onPrepareOptionsMenu(Menu menu) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001134 if (mDoneIniting) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001135 menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001136 menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
1137 menu.findItem(FILTER_APPS_ALL).setVisible(mFilterApps != FILTER_APPS_ALL);
1138 menu.findItem(FILTER_APPS_THIRD_PARTY).setVisible(
1139 mFilterApps != FILTER_APPS_THIRD_PARTY);
1140 menu.findItem(FILTER_APPS_RUNNING).setVisible(
1141 mFilterApps != FILTER_APPS_RUNNING);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001142 return true;
1143 }
1144 return false;
1145 }
1146
1147 @Override
1148 public boolean onOptionsItemSelected(MenuItem item) {
1149 int menuId = item.getItemId();
1150 sendMessageToHandler(REORDER_LIST, menuId);
1151 return true;
1152 }
1153
1154 public void onItemClick(AdapterView<?> parent, View view, int position,
1155 long id) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001156 ApplicationInfo info = (ApplicationInfo)mAppInfoAdapter.getItem(position);
1157 startApplicationDetailsActivity(info, mAppInfoAdapter.getAppStats(info.packageName));
1158 }
1159
1160 // onCancel call back for dialog thats displayed when data is being loaded
1161 public void onCancel(DialogInterface dialog) {
1162 mLoadingDlg = null;
1163 finish();
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001164 }
1165}