blob: e5311aeedd7a001f4529f535a655392580eb8a69 [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;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080020import android.app.ActivityManager;
The Android Open Source Project1feaa852009-02-10 15:44:05 -080021import android.app.AlertDialog;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080022import 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
The Android Open Source Project1feaa852009-02-10 15:44:05 -080093 OnItemClickListener, DialogInterface.OnCancelListener,
94 DialogInterface.OnClickListener {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080095 // TAG for this activity
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070096 private static final String TAG = "ManageApplications";
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070097
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080098 // log information boolean
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070099 private boolean localLOGV = Config.LOGV || false;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800100
101 // attributes used as keys when passing values to InstalledAppDetails activity
102 public static final String APP_PKG_PREFIX = "com.android.settings.";
103 public static final String APP_PKG_NAME = APP_PKG_PREFIX+"ApplicationPkgName";
104 public static final String APP_PKG_SIZE = APP_PKG_PREFIX+"size";
105 public static final String APP_CHG = APP_PKG_PREFIX+"changed";
106
107 // attribute name used in receiver for tagging names of added/deleted packages
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700108 private static final String ATTR_PKG_NAME="PackageName";
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800109 private static final String ATTR_APP_PKG_STATS="ApplicationPackageStats";
110
111 // constant value that can be used to check return code from sub activity.
112 private static final int INSTALLED_APP_DETAILS = 1;
113
114 // sort order that can be changed through the menu can be sorted alphabetically
115 // or size(descending)
116 private static final int MENU_OPTIONS_BASE = 0;
117 public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 0;
118 public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 1;
119 // Filter options used for displayed list of applications
120 public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 2;
121 public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 3;
122 public static final int FILTER_APPS_RUNNING = MENU_OPTIONS_BASE + 4;
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800123 public static final int FILTER_OPTIONS = MENU_OPTIONS_BASE + 5;
124 // Alert Dialog presented to user to find out the filter option
125 AlertDialog.Builder mAlertDlgBuilder;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800126 // sort order
127 private int mSortOrder = SORT_ORDER_ALPHA;
128 // Filter value
129 int mFilterApps = FILTER_APPS_ALL;
130
131 // Custom Adapter used for managing items in the list
132 private AppInfoAdapter mAppInfoAdapter;
133
134 // messages posted to the handler
135 private static final int HANDLER_MESSAGE_BASE = 0;
136 private static final int COMPUTE_PKG_SIZE_START = HANDLER_MESSAGE_BASE+1;
137 private static final int COMPUTE_PKG_SIZE_DONE = HANDLER_MESSAGE_BASE+2;
138 private static final int REMOVE_PKG = HANDLER_MESSAGE_BASE+3;
139 private static final int REORDER_LIST = HANDLER_MESSAGE_BASE+4;
140 private static final int ADD_PKG_START = HANDLER_MESSAGE_BASE+5;
141 private static final int ADD_PKG_DONE = HANDLER_MESSAGE_BASE+6;
142 private static final int REFRESH_ICONS = HANDLER_MESSAGE_BASE+7;
143
144 // observer object used for computing pkg sizes
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700145 private PkgSizeObserver mObserver;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800146 // local handle to PackageManager
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700147 private PackageManager mPm;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800148 // Broadcast Receiver object that receives notifications for added/deleted
149 // packages
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700150 private PackageIntentReceiver mReceiver;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800151 // atomic variable used to track if computing pkg sizes is in progress. should be volatile?
152
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700153 private boolean mDoneIniting = false;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800154 // default icon thats used when displaying applications initially before resource info is
155 // retrieved
156 private Drawable mDefaultAppIcon;
157
158 // temporary dialog displayed while the application info loads
159 private ProgressDialog mLoadingDlg = null;
160
161 // compute index used to track the application size computations
162 private int mComputeIndex;
163
164 // Size resource used for packages whose size computation failed for some reason
165 private CharSequence mInvalidSizeStr;
166 private CharSequence mComputingSizeStr;
167
168 // map used to store list of added and removed packages. Immutable Boolean
169 // variables indicate if a package has been added or removed. If a package is
170 // added or deleted multiple times a single entry with the latest operation will
171 // be recorded in the map.
172 private Map<String, Boolean> mAddRemoveMap;
173
174 // layout inflater object used to inflate views
175 private LayoutInflater mInflater;
176
177 // invalid size value used initially and also when size retrieval through PackageManager
178 // fails for whatever reason
179 private static final int SIZE_INVALID = -1;
180
181 // debug boolean variable to test delays from PackageManager API's
182 private boolean DEBUG_PKG_DELAY = false;
183
184 // Thread to load resources
185 ResourceLoaderThread mResourceThread;
186
187 String mCurrentPkgName;
188
189 //TODO implement a cache system
190 private Map<String, AppInfo> mAppPropCache;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700191
192 /*
193 * Handler class to handle messages for various operations
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800194 * Most of the operations that effect Application related data
195 * are posted as messages to the handler to avoid synchronization
196 * when accessing these structures.
197 * When the size retrieval gets kicked off for the first time, a COMPUTE_PKG_SIZE_START
198 * message is posted to the handler which invokes the getSizeInfo for the pkg at index 0
199 * When the PackageManager's asynchronous call back through
200 * PkgSizeObserver.onGetStatsCompleted gets invoked, the application resources like
201 * label, description, icon etc., is loaded in the same thread and these values are
202 * set on the observer. The observer then posts a COMPUTE_PKG_SIZE_DONE message
203 * to the handler. This information is updated on the AppInfoAdapter associated with
204 * the list view of this activity and size info retrieval is initiated for the next package as
205 * indicated by mComputeIndex
206 * When a package gets added while the activity has focus, the PkgSizeObserver posts
207 * ADD_PKG_START message to the handler. If the computation is not in progress, the size
208 * is retrieved for the newly added package through the observer object and the newly
209 * installed app info is updated on the screen. If the computation is still in progress
210 * the package is added to an internal structure and action deferred till the computation
211 * is done for all the packages.
212 * When a package gets deleted, REMOVE_PKG is posted to the handler
213 * if computation is not in progress(as indicated by
214 * mDoneIniting), the package is deleted from the displayed list of apps. If computation is
215 * still in progress the package is added to an internal structure and action deferred till
216 * the computation is done for all packages.
217 * When the sizes of all packages is computed, the newly
218 * added or removed packages are processed in order.
219 * If the user changes the order in which these applications are viewed by hitting the
220 * menu key, REORDER_LIST message is posted to the handler. this sorts the list
221 * of items based on the sort order.
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700222 */
223 private Handler mHandler = new Handler() {
224 public void handleMessage(Message msg) {
225 PackageStats ps;
226 ApplicationInfo info;
227 Bundle data;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800228 String pkgName = null;
229 AppInfo appInfo;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700230 data = msg.getData();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800231 if(data != null) {
232 pkgName = data.getString(ATTR_PKG_NAME);
233 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700234 switch (msg.what) {
235 case COMPUTE_PKG_SIZE_START:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800236 if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_START");
237 setProgressBarIndeterminateVisibility(true);
238 mComputeIndex = 0;
239 initAppList(mFilterApps);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700240 break;
241 case COMPUTE_PKG_SIZE_DONE:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800242 if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
243 if(pkgName == null) {
244 Log.w(TAG, "Ignoring message");
245 break;
246 }
247 ps = data.getParcelable(ATTR_APP_PKG_STATS);
248 if(ps == null) {
249 Log.i(TAG, "Invalid package stats for package:"+pkgName);
250 } else {
251 int pkgId = mAppInfoAdapter.getIndex(pkgName);
252 if(mComputeIndex != pkgId) {
253 //spurious call from stale observer
254 Log.w(TAG, "Stale call back from PkgSizeObserver");
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700255 break;
256 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800257 mAppInfoAdapter.updateAppSize(pkgName, ps);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700258 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800259 mComputeIndex++;
260 if (mComputeIndex < mAppInfoAdapter.getCount()) {
261 // initiate compute package size for next pkg in list
262 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
263 mComputeIndex),
264 COMPUTE_PKG_SIZE_DONE);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700265 } else {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800266 // check for added/removed packages
267 Set<String> keys = mAddRemoveMap.keySet();
268 Iterator<String> iter = keys.iterator();
269 List<String> removeList = new ArrayList<String>();
270 boolean added = false;
271 boolean removed = false;
272 while (iter.hasNext()) {
273 String key = iter.next();
274 if (mAddRemoveMap.get(key) == Boolean.TRUE) {
275 // add
276 try {
277 info = mPm.getApplicationInfo(key, 0);
278 mAppInfoAdapter.addApplicationInfo(info);
279 added = true;
280 } catch (NameNotFoundException e) {
281 Log.w(TAG, "Invalid added package:"+key+" Ignoring entry");
282 }
283 } else {
284 // remove
285 removeList.add(key);
286 removed = true;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700287 }
288 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800289 // remove uninstalled packages from list
290 if (removed) {
291 mAppInfoAdapter.removeFromList(removeList);
292 }
293 // handle newly installed packages
294 if (added) {
295 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
296 mComputeIndex),
297 COMPUTE_PKG_SIZE_DONE);
298 } else {
299 // end computation here
300 mDoneIniting = true;
301 mAppInfoAdapter.sortList(mSortOrder);
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800302 setProgressBarIndeterminateVisibility(false);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800303 }
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);
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800384 }
385 // initiate compute pkg sizes
386 if (localLOGV) Log.i(TAG, "Initiating compute sizes for first time");
387 mObserver = new PkgSizeObserver();
388 if (mAppInfoAdapter.getCount() > 0) {
389 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(0),
390 COMPUTE_PKG_SIZE_DONE);
391 } else {
392 mDoneIniting = true;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800393 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700394 default:
395 break;
396 }
397 }
398 };
399
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800400 List<ApplicationInfo> getInstalledApps(int filterOption) {
401 List<ApplicationInfo> installedAppList = mPm.getInstalledApplications(
402 PackageManager.GET_UNINSTALLED_PACKAGES);
403 if (installedAppList == null) {
404 return new ArrayList<ApplicationInfo> ();
405 }
406 if (filterOption == FILTER_APPS_THIRD_PARTY) {
407 List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
408 for (ApplicationInfo appInfo : installedAppList) {
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800409 boolean flag = false;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800410 if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800411 // Updated system app
412 flag = true;
413 } else if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
414 // Non-system app
415 flag = true;
416 }
417 if (flag) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800418 appList.add(appInfo);
419 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700420 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800421 return appList;
422 } else if (filterOption == FILTER_APPS_RUNNING) {
423 List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
424 List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
425 if ((procList == null) || (procList.size() == 0)) {
426 return appList;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700427 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800428 // Retrieve running processes from ActivityManager
429 for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
430 if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
431 int size = appProcInfo.pkgList.length;
432 for (int i = 0; i < size; i++) {
433 ApplicationInfo appInfo = null;
434 try {
435 appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i],
436 PackageManager.GET_UNINSTALLED_PACKAGES);
437 } catch (NameNotFoundException e) {
438 Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]);
439 continue;
440 }
441 if(appInfo != null) {
442 appList.add(appInfo);
443 }
444 }
445 }
446 }
447 return appList;
448 } else {
449 return installedAppList;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700450 }
451 }
452
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800453 private List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesList() {
454 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
455 return am.getRunningAppProcesses();
456 }
457
458 // some initialization code used when kicking off the size computation
459 private void initAppList(int filterOption) {
460 mDoneIniting = false;
461 // Initialize lists
462 List<ApplicationInfo> appList = getInstalledApps(filterOption);
463 mAddRemoveMap = new TreeMap<String, Boolean>();
464 mAppInfoAdapter = new AppInfoAdapter(this, appList);
465 dismissLoadingMsg();
466 // get list and set listeners and adapter
467 ListView lv= (ListView) findViewById(android.R.id.list);
468 lv.setOnItemClickListener(this);
469 lv.setSaveEnabled(true);
470 lv.setItemsCanFocus(true);
471 lv.setOnItemClickListener(this);
472 lv.setAdapter(mAppInfoAdapter);
473 // register receiver
474 mReceiver = new PackageIntentReceiver();
475 mReceiver.registerReceiver();
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800476 //load resources now
477 if(mResourceThread.isAlive()) {
478 mResourceThread.interrupt();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800479 }
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800480 mResourceThread.loadAllResources(appList);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800481 }
482
483 // internal structure used to track added and deleted packages when
484 // the activity has focus
485 class AddRemoveInfo {
486 String pkgName;
487 boolean add;
488 public AddRemoveInfo(String pPkgName, boolean pAdd) {
489 pkgName = pPkgName;
490 add = pAdd;
491 }
492 }
493
494 class ResourceLoaderThread extends Thread {
495 List<ApplicationInfo> mAppList;
496
497 void loadAllResources(List<ApplicationInfo> appList) {
498 if(appList == null || appList.size() <= 0) {
499 Log.w(TAG, "Empty or null application list");
500 return;
501 }
502 mAppList = appList;
503 start();
504 }
505
506 public void run() {
507 Map<String, AppInfo> iconMap = new HashMap<String, AppInfo>();
508 for (ApplicationInfo appInfo : mAppList) {
509 CharSequence appName = appInfo.loadLabel(mPm);
510 Drawable appIcon = appInfo.loadIcon(mPm);
511 iconMap.put(appInfo.packageName,
512 new AppInfo(appInfo.packageName, appName, appIcon));
513 }
514 Message msg = mHandler.obtainMessage(REFRESH_ICONS);
515 msg.obj = iconMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700516 mHandler.sendMessage(msg);
517 }
518 }
519
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800520 /* Internal class representing an application or packages displayable attributes
521 *
522 */
523 class AppInfo {
524 public String pkgName;
525 int index;
526 public CharSequence appName;
527 public Drawable appIcon;
528 public CharSequence appSize;
529 public PackageStats appStats;
530
531 public void refreshIcon(AppInfo pInfo) {
532 appName = pInfo.appName;
533 appIcon = pInfo.appIcon;
534 }
535
536 public AppInfo(String pName, CharSequence aName, Drawable aIcon) {
537 index = -1;
538 pkgName = pName;
539 appName = aName;
540 appIcon = aIcon;
541 appStats = null;
542 appSize = mComputingSizeStr;
543 }
544
545 public AppInfo(String pName, int pIndex, CharSequence aName, Drawable aIcon,
546 PackageStats ps) {
547 index = pIndex;
548 pkgName = pName;
549 appName = aName;
550 appIcon = aIcon;
551 if(ps == null) {
552 appSize = mComputingSizeStr;
553 } else {
554 appStats = ps;
555 appSize = getSizeStr();
556 }
557 }
558 public void setSize(PackageStats ps) {
559 appStats = ps;
560 if (ps != null) {
561 appSize = getSizeStr();
562 }
563 }
564 public long getTotalSize() {
565 PackageStats ps = appStats;
566 if (ps != null) {
567 return ps.cacheSize+ps.codeSize+ps.dataSize;
568 }
569 return SIZE_INVALID;
570 }
571
572 private String getSizeStr() {
573 PackageStats ps = appStats;
574 String retStr = "";
575 // insert total size information into map to display in view
576 // at this point its guaranteed that ps is not null. but checking anyway
577 if (ps != null) {
578 long size = getTotalSize();
579 if (size == SIZE_INVALID) {
580 return mInvalidSizeStr.toString();
581 }
582 return Formatter.formatFileSize(ManageApplications.this, size);
583 }
584 return retStr;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700585 }
586 }
587
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800588 // View Holder used when displaying views
589 static class AppViewHolder {
590 TextView appName;
591 ImageView appIcon;
592 TextView appSize;
593 }
594
595 /* Custom adapter implementation for the ListView
596 * This adapter maintains a map for each displayed application and its properties
597 * An index value on each AppInfo object indicates the correct position or index
598 * in the list. If the list gets updated dynamically when the user is viewing the list of
599 * applications, we need to return the correct index of position. This is done by mapping
600 * the getId methods via the package name into the internal maps and indices.
601 * The order of applications in the list is mirrored in mAppLocalList
602 */
603 class AppInfoAdapter extends BaseAdapter {
604 private Map<String, AppInfo> mAppPropMap;
605 private List<ApplicationInfo> mAppLocalList;
606 ApplicationInfo.DisplayNameComparator mAlphaComparator;
607 AppInfoComparator mSizeComparator;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700608
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800609 private AppInfo getFromCache(String packageName) {
610 if(mAppPropCache == null) {
611 return null;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700612 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800613 return mAppPropCache.get(packageName);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700614 }
615
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800616 public AppInfoAdapter(Context c, List<ApplicationInfo> appList) {
617 mAppLocalList = appList;
618 boolean useCache = false;
619 int sortOrder = SORT_ORDER_ALPHA;
620 int imax = mAppLocalList.size();
621 if(mAppPropCache != null) {
622 useCache = true;
623 // Activity has been resumed. can use the cache to populate values initially
624 mAppPropMap = mAppPropCache;
625 sortOrder = mSortOrder;
626 }
627 sortAppList(sortOrder);
628 // Recreate property map
629 mAppPropMap = new TreeMap<String, AppInfo>();
630 for (int i = 0; i < imax; i++) {
631 ApplicationInfo info = mAppLocalList.get(i);
632 AppInfo aInfo = getFromCache(info.packageName);
633 if(aInfo == null){
634 aInfo = new AppInfo(info.packageName, i,
635 info.packageName, mDefaultAppIcon, null);
636 } else {
637 aInfo.index = i;
638 }
639 mAppPropMap.put(info.packageName, aInfo);
640 }
641 }
642
643 public int getCount() {
644 return mAppLocalList.size();
645 }
646
647 public Object getItem(int position) {
648 return mAppLocalList.get(position);
649 }
650
651 /*
652 * This method returns the index of the package position in the application list
653 */
654 public int getIndex(String pkgName) {
655 if(pkgName == null) {
656 Log.w(TAG, "Getting index of null package in List Adapter");
657 }
658 int imax = mAppLocalList.size();
659 ApplicationInfo appInfo;
660 for(int i = 0; i < imax; i++) {
661 appInfo = mAppLocalList.get(i);
662 if(appInfo.packageName.equalsIgnoreCase(pkgName)) {
663 return i;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700664 }
665 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800666 return -1;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700667 }
668
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800669 public ApplicationInfo getApplicationInfo(int position) {
670 int imax = mAppLocalList.size();
671 if( (position < 0) || (position >= imax)) {
672 Log.w(TAG, "Position out of bounds in List Adapter");
673 return null;
674 }
675 return mAppLocalList.get(position);
676 }
677
678 public void addApplicationInfo(ApplicationInfo info) {
679 if(info == null) {
680 Log.w(TAG, "Ignoring null add in List Adapter");
681 return;
682 }
683 mAppLocalList.add(info);
684 }
685
686 public long getItemId(int position) {
687 int imax = mAppLocalList.size();
688 if( (position < 0) || (position >= imax)) {
689 Log.w(TAG, "Position out of bounds in List Adapter");
690 return -1;
691 }
692 return mAppPropMap.get(mAppLocalList.get(position).packageName).index;
693 }
694
695 public List<ApplicationInfo> getAppList() {
696 return mAppLocalList;
697 }
698
699 public View getView(int position, View convertView, ViewGroup parent) {
700 // A ViewHolder keeps references to children views to avoid unneccessary calls
701 // to findViewById() on each row.
702 AppViewHolder holder;
703
704 // When convertView is not null, we can reuse it directly, there is no need
705 // to reinflate it. We only inflate a new View when the convertView supplied
706 // by ListView is null.
707 if (convertView == null) {
708 convertView = mInflater.inflate(R.layout.manage_applications_item, null);
709
710 // Creates a ViewHolder and store references to the two children views
711 // we want to bind data to.
712 holder = new AppViewHolder();
713 holder.appName = (TextView) convertView.findViewById(R.id.app_name);
714 holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
715 holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
716 convertView.setTag(holder);
717 } else {
718 // Get the ViewHolder back to get fast access to the TextView
719 // and the ImageView.
720 holder = (AppViewHolder) convertView.getTag();
721 }
722
723 // Bind the data efficiently with the holder
724 ApplicationInfo appInfo = mAppLocalList.get(position);
725 AppInfo mInfo = mAppPropMap.get(appInfo.packageName);
726 if(mInfo != null) {
727 if(mInfo.appName != null) {
728 holder.appName.setText(mInfo.appName);
729 }
730 if(mInfo.appIcon != null) {
731 holder.appIcon.setImageDrawable(mInfo.appIcon);
732 }
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800733 if (mInfo.appSize != null) {
734 holder.appSize.setText(mInfo.appSize);
735 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800736 } else {
737 Log.w(TAG, "No info for package:"+appInfo.packageName+" in property map");
738 }
739 return convertView;
740 }
741
742 private void adjustIndex() {
743 int imax = mAppLocalList.size();
744 ApplicationInfo info;
745 for (int i = 0; i < imax; i++) {
746 info = mAppLocalList.get(i);
747 mAppPropMap.get(info.packageName).index = i;
748 }
749 }
750
751 public void sortAppList(int sortOrder) {
752 Collections.sort(mAppLocalList, getAppComparator(sortOrder));
753 }
754
755 public void sortList(int sortOrder) {
756 sortAppList(sortOrder);
757 adjustIndex();
758 notifyDataSetChanged();
759 }
760
761 public boolean resetAppList(int filterOption, List<ApplicationInfo> appList) {
762 // Create application list based on the filter value
763 mAppLocalList = appList;
764 // Check for all properties in map before sorting. Populate values from cache
765 for(ApplicationInfo applicationInfo : mAppLocalList) {
766 AppInfo appInfo = mAppPropMap.get(applicationInfo.packageName);
767 if(appInfo == null) {
768 AppInfo rInfo = getFromCache(applicationInfo.packageName);
769 if(rInfo == null) {
770 // Need to load resources again. Inconsistency somewhere
771 return false;
772 }
773 mAppPropMap.put(applicationInfo.packageName, rInfo);
774 }
775 }
776 sortList(mSortOrder);
777 return true;
778 }
779
780 private Comparator<ApplicationInfo> getAppComparator(int sortOrder) {
781 if (sortOrder == SORT_ORDER_ALPHA) {
782 // Lazy initialization
783 if (mAlphaComparator == null) {
784 mAlphaComparator = new ApplicationInfo.DisplayNameComparator(mPm);
785 }
786 return mAlphaComparator;
787 }
788 // Lazy initialization
789 if(mSizeComparator == null) {
790 mSizeComparator = new AppInfoComparator(mAppPropMap);
791 }
792 return mSizeComparator;
793 }
794
795 public void updateAppsResourceInfo(Map<String, AppInfo> iconMap) {
796 if(iconMap == null) {
797 Log.w(TAG, "Null iconMap when refreshing icon in List Adapter");
798 return;
799 }
800 boolean changed = false;
801 for (ApplicationInfo info : mAppLocalList) {
802 AppInfo pInfo = iconMap.get(info.packageName);
803 if(pInfo != null) {
804 AppInfo aInfo = mAppPropMap.get(info.packageName);
805 aInfo.refreshIcon(pInfo);
806 changed = true;
807 }
808 }
809 if(changed) {
810 notifyDataSetChanged();
811 }
812 }
813
814 public void addToList(String pkgName, PackageStats ps) {
815 if(pkgName == null) {
816 Log.w(TAG, "Adding null pkg to List Adapter");
817 return;
818 }
819 ApplicationInfo info;
820 try {
821 info = mPm.getApplicationInfo(pkgName, 0);
822 } catch (NameNotFoundException e) {
823 Log.w(TAG, "Ignoring non-existent package:"+pkgName);
824 return;
825 }
826 if(info == null) {
827 // Nothing to do log error message and return
828 Log.i(TAG, "Null ApplicationInfo for package:"+pkgName);
829 return;
830 }
831 // Binary search returns a negative index (ie --index) of the position where
832 // this might be inserted.
833 int newIdx = Collections.binarySearch(mAppLocalList, info,
834 getAppComparator(mSortOrder));
835 if(newIdx >= 0) {
836 Log.i(TAG, "Strange. Package:"+pkgName+" is not new");
837 return;
838 }
839 // New entry
840 newIdx = -newIdx-1;
841 mAppLocalList.add(newIdx, info);
842 mAppPropMap.put(info.packageName, new AppInfo(pkgName, newIdx,
843 info.loadLabel(mPm), info.loadIcon(mPm), ps));
844 adjustIndex();
845 notifyDataSetChanged();
846 }
847
848 public void removeFromList(List<String> pkgNames) {
849 if(pkgNames == null) {
850 Log.w(TAG, "Removing null pkg list from List Adapter");
851 return;
852 }
853 int imax = mAppLocalList.size();
854 boolean found = false;
855 ApplicationInfo info;
856 int i, k;
857 String pkgName;
858 int kmax = pkgNames.size();
859 if(kmax <= 0) {
860 Log.w(TAG, "Removing empty pkg list from List Adapter");
861 return;
862 }
863 int idxArr[] = new int[kmax];
864 for (k = 0; k < kmax; k++) {
865 idxArr[k] = -1;
866 }
867 for (i = 0; i < imax; i++) {
868 info = mAppLocalList.get(i);
869 for (k = 0; k < kmax; k++) {
870 pkgName = pkgNames.get(k);
871 if (info.packageName.equalsIgnoreCase(pkgName)) {
872 idxArr[k] = i;
873 found = true;
874 break;
875 }
876 }
877 }
878 // Sort idxArr
879 Arrays.sort(idxArr);
880 // remove the packages based on decending indices
881 for (k = kmax-1; k >= 0; k--) {
882 // Check if package has been found in the list of existing apps first
883 if(idxArr[k] == -1) {
884 break;
885 }
886 info = mAppLocalList.get(idxArr[k]);
887 mAppLocalList.remove(idxArr[k]);
888 mAppPropMap.remove(info.packageName);
889 if (localLOGV) Log.i(TAG, "Removed pkg:"+info.packageName+ " list");
890 }
891 if (found) {
892 adjustIndex();
893 notifyDataSetChanged();
894 }
895 }
896
897 public void updateAppSize(String pkgName, PackageStats ps) {
898 if(pkgName == null) {
899 return;
900 }
901 AppInfo entry = mAppPropMap.get(pkgName);
902 if (entry == null) {
903 Log.w(TAG, "Entry for package:"+pkgName+"doesnt exist in map");
904 return;
905 }
906 // Copy the index into the newly updated entry
907 entry.setSize(ps);
908 notifyDataSetChanged();
909 }
910
911 public PackageStats getAppStats(String pkgName) {
912 if(pkgName == null) {
913 return null;
914 }
915 AppInfo entry = mAppPropMap.get(pkgName);
916 if (entry == null) {
917 return null;
918 }
919 return entry.appStats;
920 }
921 }
922
923 /*
924 * Utility method to clear messages to Handler
925 * We need'nt synchronize on the Handler since posting messages is guaranteed
926 * to be thread safe. Even if the other thread that retrieves package sizes
927 * posts a message, we do a cursory check of validity on mAppInfoAdapter's applist
928 */
929 private void clearMessagesInHandler() {
930 mHandler.removeMessages(COMPUTE_PKG_SIZE_START);
931 mHandler.removeMessages(COMPUTE_PKG_SIZE_DONE);
932 mHandler.removeMessages(REMOVE_PKG);
933 mHandler.removeMessages(REORDER_LIST);
934 mHandler.removeMessages(ADD_PKG_START);
935 mHandler.removeMessages(ADD_PKG_DONE);
936 }
937
938 private void sendMessageToHandler(int msgId, int arg1) {
939 Message msg = mHandler.obtainMessage(msgId);
940 msg.arg1 = arg1;
941 mHandler.sendMessage(msg);
942 }
943
944 private void sendMessageToHandler(int msgId, Bundle data) {
945 Message msg = mHandler.obtainMessage(msgId);
946 msg.setData(data);
947 mHandler.sendMessage(msg);
948 }
949
950 private void sendMessageToHandler(int msgId) {
951 mHandler.sendEmptyMessage(msgId);
952 }
953
954 /*
955 * Stats Observer class used to compute package sizes and retrieve size information
956 * PkgSizeOberver is the call back thats used when invoking getPackageSizeInfo on
957 * PackageManager. The values in call back onGetStatsCompleted are validated
958 * and the specified message is passed to mHandler. The package name
959 * and the AppInfo object corresponding to the package name are set on the message
960 */
961 class PkgSizeObserver extends IPackageStatsObserver.Stub {
962 private ApplicationInfo mAppInfo;
963 private int mMsgId;
964 public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) {
965 if(DEBUG_PKG_DELAY) {
966 try {
967 Thread.sleep(10*1000);
968 } catch (InterruptedException e) {
969 }
970 }
971 AppInfo appInfo = null;
972 Bundle data = new Bundle();
973 data.putString(ATTR_PKG_NAME, mAppInfo.packageName);
974 if(pSucceeded && pStats != null) {
975 if (localLOGV) Log.i(TAG, "onGetStatsCompleted::"+pStats.packageName+", ("+
976 pStats.cacheSize+","+
977 pStats.codeSize+", "+pStats.dataSize);
978 data.putParcelable(ATTR_APP_PKG_STATS, pStats);
979 } else {
980 Log.w(TAG, "Invalid package stats from PackageManager");
981 }
982 //post message to Handler
983 Message msg = mHandler.obtainMessage(mMsgId, data);
984 msg.setData(data);
985 mHandler.sendMessage(msg);
986 }
987
988 public void invokeGetSizeInfo(ApplicationInfo pAppInfo, int msgId) {
989 if(pAppInfo == null || pAppInfo.packageName == null) {
990 return;
991 }
992 if(localLOGV) Log.i(TAG, "Invoking getPackageSizeInfo for package:"+
993 pAppInfo.packageName);
994 mMsgId = msgId;
995 mAppInfo = pAppInfo;
996 mPm.getPackageSizeInfo(pAppInfo.packageName, this);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700997 }
998 }
999
1000 /**
1001 * Receives notifications when applications are added/removed.
1002 */
1003 private class PackageIntentReceiver extends BroadcastReceiver {
1004 void registerReceiver() {
1005 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1006 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1007 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1008 filter.addDataScheme("package");
1009 ManageApplications.this.registerReceiver(this, filter);
1010 }
1011 @Override
1012 public void onReceive(Context context, Intent intent) {
1013 String actionStr = intent.getAction();
1014 Uri data = intent.getData();
1015 String pkgName = data.getEncodedSchemeSpecificPart();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001016 if (localLOGV) Log.i(TAG, "action:"+actionStr+", for package:"+pkgName);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001017 updatePackageList(actionStr, pkgName);
1018 }
1019 }
1020
1021 private void updatePackageList(String actionStr, String pkgName) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001022 // technically we dont have to invoke handler since onReceive is invoked on
1023 // the main thread but doing it here for better clarity
1024 if (Intent.ACTION_PACKAGE_ADDED.equalsIgnoreCase(actionStr)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001025 Bundle data = new Bundle();
1026 data.putString(ATTR_PKG_NAME, pkgName);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001027 sendMessageToHandler(ADD_PKG_START, data);
1028 } else if (Intent.ACTION_PACKAGE_REMOVED.equalsIgnoreCase(actionStr)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001029 Bundle data = new Bundle();
1030 data.putString(ATTR_PKG_NAME, pkgName);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001031 sendMessageToHandler(REMOVE_PKG, data);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001032 }
1033 }
1034
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001035
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001036 @Override
1037 protected void onCreate(Bundle savedInstanceState) {
1038 super.onCreate(savedInstanceState);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001039 Intent lIntent = getIntent();
1040 String action = lIntent.getAction();
1041 if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001042 mSortOrder = SORT_ORDER_SIZE;
1043 }
1044 mPm = getPackageManager();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001045 // initialize some window features
1046 requestWindowFeature(Window.FEATURE_RIGHT_ICON);
1047 requestWindowFeature(Window.FEATURE_PROGRESS);
1048 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
1049 // init mLoadingDlg
1050 mLoadingDlg = new ProgressDialog(this);
1051 mLoadingDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
1052 mLoadingDlg.setMessage(getText(R.string.loading));
1053 mLoadingDlg.setIndeterminate(true);
1054 mLoadingDlg.setOnCancelListener(this);
1055 mDefaultAppIcon =Resources.getSystem().getDrawable(
1056 com.android.internal.R.drawable.sym_def_app_icon);
1057 mInvalidSizeStr = getText(R.string.invalid_size_value);
1058 mComputingSizeStr = getText(R.string.computing_size);
1059 // initialize the inflater
1060 mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1061 }
1062
1063 private void showLoadingMsg() {
1064 if (mLoadingDlg != null) {
1065 if(localLOGV) Log.i(TAG, "Displaying Loading message");
1066 mLoadingDlg.show();
1067 }
1068 }
1069
1070 private void dismissLoadingMsg() {
1071 if ((mLoadingDlg != null) && (mLoadingDlg.isShowing())) {
1072 if(localLOGV) Log.i(TAG, "Dismissing Loading message");
1073 mLoadingDlg.dismiss();
1074 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001075 }
1076
1077 @Override
1078 public void onStart() {
1079 super.onStart();
1080 setContentView(R.layout.compute_sizes);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001081 showLoadingMsg();
1082 // Create a thread to load resources
1083 mResourceThread = new ResourceLoaderThread();
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001084 sendMessageToHandler(COMPUTE_PKG_SIZE_START);
1085 }
1086
1087 @Override
1088 public void onStop() {
1089 super.onStop();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001090 // clear all messages related to application list
1091 clearMessagesInHandler();
1092 // register receiver here
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001093 unregisterReceiver(mReceiver);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001094 mAppPropCache = mAppInfoAdapter.mAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001095 }
1096
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001097 /*
1098 * comparator class used to sort AppInfo objects based on size
1099 */
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001100 public static class AppInfoComparator implements Comparator<ApplicationInfo> {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001101 public AppInfoComparator(Map<String, AppInfo> pAppPropMap) {
1102 mAppPropMap= pAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001103 }
1104
1105 public final int compare(ApplicationInfo a, ApplicationInfo b) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001106 AppInfo ainfo = mAppPropMap.get(a.packageName);
1107 AppInfo binfo = mAppPropMap.get(b.packageName);
1108 long atotal = ainfo.getTotalSize();
1109 long btotal = binfo.getTotalSize();
1110 long ret = atotal - btotal;
1111 // negate result to sort in descending order
1112 if (ret < 0) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001113 return 1;
1114 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001115 if (ret == 0) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001116 return 0;
1117 }
1118 return -1;
1119 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001120 private Map<String, AppInfo> mAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001121 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001122
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001123 // utility method used to start sub activity
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001124 private void startApplicationDetailsActivity(ApplicationInfo info, PackageStats ps) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001125 // Create intent to start new activity
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001126 Intent intent = new Intent(Intent.ACTION_VIEW);
1127 intent.setClass(this, InstalledAppDetails.class);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001128 mCurrentPkgName = info.packageName;
1129 intent.putExtra(APP_PKG_NAME, mCurrentPkgName);
1130 intent.putExtra(APP_PKG_SIZE, ps);
1131 // start new activity to display extended information
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001132 startActivityForResult(intent, INSTALLED_APP_DETAILS);
1133 }
1134
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001135 @Override
1136 public boolean onCreateOptionsMenu(Menu menu) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001137 menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha)
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001138 .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001139 menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
1140 .setIcon(android.R.drawable.ic_menu_sort_by_size);
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001141 menu.add(0, FILTER_OPTIONS, 3, R.string.filter);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001142 return true;
1143 }
1144
1145 @Override
1146 public boolean onPrepareOptionsMenu(Menu menu) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001147 if (mDoneIniting) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001148 menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001149 menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001150 menu.findItem(FILTER_OPTIONS).setVisible(true);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001151 return true;
1152 }
1153 return false;
1154 }
1155
1156 @Override
1157 public boolean onOptionsItemSelected(MenuItem item) {
1158 int menuId = item.getItemId();
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001159 if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
1160 sendMessageToHandler(REORDER_LIST, menuId);
1161 } else if (menuId == FILTER_OPTIONS) {
1162 if (mAlertDlgBuilder == null) {
1163 mAlertDlgBuilder = new AlertDialog.Builder(this).
1164 setTitle(R.string.filter_dlg_title).
1165 setNeutralButton(R.string.cancel, this).
1166 setSingleChoiceItems(new CharSequence[] {getText(R.string.filter_apps_all),
1167 getText(R.string.filter_apps_running),
1168 getText(R.string.filter_apps_third_party)},
1169 -1, this);
1170 }
1171 mAlertDlgBuilder.show();
1172 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001173 return true;
1174 }
1175
1176 public void onItemClick(AdapterView<?> parent, View view, int position,
1177 long id) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001178 ApplicationInfo info = (ApplicationInfo)mAppInfoAdapter.getItem(position);
1179 startApplicationDetailsActivity(info, mAppInfoAdapter.getAppStats(info.packageName));
1180 }
1181
1182 // onCancel call back for dialog thats displayed when data is being loaded
1183 public void onCancel(DialogInterface dialog) {
1184 mLoadingDlg = null;
1185 finish();
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001186 }
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001187
1188 public void onClick(DialogInterface dialog, int which) {
1189 int newOption;
1190 switch (which) {
1191 // Make sure that values of 0, 1, 2 match options all, running, third_party when
1192 // created via the AlertDialog.Builder
1193 case 0:
1194 newOption = FILTER_APPS_ALL;
1195 break;
1196 case 1:
1197 newOption = FILTER_APPS_RUNNING;
1198 break;
1199 case 2:
1200 newOption = FILTER_APPS_THIRD_PARTY;
1201 break;
1202 default:
1203 return;
1204 }
1205 sendMessageToHandler(REORDER_LIST, newOption);
1206 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001207}