blob: 512e547e6d428c5a234f9a012c12260f40ec8eb8 [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.
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -080089 * 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 Projecta578a6c2009-03-03 14:04:35 -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";
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -0800104 public static final String APP_PKG_SIZE = APP_PKG_PREFIX+"size";
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800105 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
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -0800125 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;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800136 private static final int INIT_PKG_INFO = HANDLER_MESSAGE_BASE+1;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800137 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;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800143 private static final int NEXT_LOAD_STEP = HANDLER_MESSAGE_BASE+8;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800144
145 // observer object used for computing pkg sizes
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700146 private PkgSizeObserver mObserver;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800147 // local handle to PackageManager
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700148 private PackageManager mPm;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800149 // Broadcast Receiver object that receives notifications for added/deleted
150 // packages
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700151 private PackageIntentReceiver mReceiver;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800152 // atomic variable used to track if computing pkg sizes is in progress. should be volatile?
153
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800154 private boolean mComputeSizes = false;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800155 // default icon thats used when displaying applications initially before resource info is
156 // retrieved
157 private Drawable mDefaultAppIcon;
158
159 // temporary dialog displayed while the application info loads
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -0800160 private ProgressDialog mLoadingDlg = null;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800161
162 // compute index used to track the application size computations
163 private int mComputeIndex;
164
165 // Size resource used for packages whose size computation failed for some reason
166 private CharSequence mInvalidSizeStr;
167 private CharSequence mComputingSizeStr;
168
169 // map used to store list of added and removed packages. Immutable Boolean
170 // variables indicate if a package has been added or removed. If a package is
171 // added or deleted multiple times a single entry with the latest operation will
172 // be recorded in the map.
173 private Map<String, Boolean> mAddRemoveMap;
174
175 // layout inflater object used to inflate views
176 private LayoutInflater mInflater;
177
178 // invalid size value used initially and also when size retrieval through PackageManager
179 // fails for whatever reason
180 private static final int SIZE_INVALID = -1;
181
182 // debug boolean variable to test delays from PackageManager API's
183 private boolean DEBUG_PKG_DELAY = false;
184
185 // Thread to load resources
186 ResourceLoaderThread mResourceThread;
187
188 String mCurrentPkgName;
189
190 //TODO implement a cache system
191 private Map<String, AppInfo> mAppPropCache;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700192
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800193 // empty message displayed when list is empty
194 private TextView mEmptyView;
195
196 // Boolean variables indicating state
197 private boolean mLoadLabels = false;
198 private boolean mSizesFirst = false;
199
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700200 /*
201 * Handler class to handle messages for various operations
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800202 * Most of the operations that effect Application related data
203 * are posted as messages to the handler to avoid synchronization
204 * when accessing these structures.
205 * When the size retrieval gets kicked off for the first time, a COMPUTE_PKG_SIZE_START
206 * message is posted to the handler which invokes the getSizeInfo for the pkg at index 0
207 * When the PackageManager's asynchronous call back through
208 * PkgSizeObserver.onGetStatsCompleted gets invoked, the application resources like
209 * label, description, icon etc., is loaded in the same thread and these values are
210 * set on the observer. The observer then posts a COMPUTE_PKG_SIZE_DONE message
211 * to the handler. This information is updated on the AppInfoAdapter associated with
212 * the list view of this activity and size info retrieval is initiated for the next package as
213 * indicated by mComputeIndex
214 * When a package gets added while the activity has focus, the PkgSizeObserver posts
215 * ADD_PKG_START message to the handler. If the computation is not in progress, the size
216 * is retrieved for the newly added package through the observer object and the newly
217 * installed app info is updated on the screen. If the computation is still in progress
218 * the package is added to an internal structure and action deferred till the computation
219 * is done for all the packages.
220 * When a package gets deleted, REMOVE_PKG is posted to the handler
221 * if computation is not in progress(as indicated by
222 * mDoneIniting), the package is deleted from the displayed list of apps. If computation is
223 * still in progress the package is added to an internal structure and action deferred till
224 * the computation is done for all packages.
225 * When the sizes of all packages is computed, the newly
226 * added or removed packages are processed in order.
227 * If the user changes the order in which these applications are viewed by hitting the
228 * menu key, REORDER_LIST message is posted to the handler. this sorts the list
229 * of items based on the sort order.
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700230 */
231 private Handler mHandler = new Handler() {
232 public void handleMessage(Message msg) {
233 PackageStats ps;
234 ApplicationInfo info;
235 Bundle data;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800236 String pkgName = null;
237 AppInfo appInfo;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700238 data = msg.getData();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800239 if(data != null) {
240 pkgName = data.getString(ATTR_PKG_NAME);
241 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700242 switch (msg.what) {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800243 case INIT_PKG_INFO:
244 if(localLOGV) Log.i(TAG, "Message INIT_PKG_INFO");
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -0800245 setProgressBarIndeterminateVisibility(true);
246 mComputeIndex = 0;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800247 // Retrieve the package list and init some structures
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800248 initAppList(mFilterApps);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800249 mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700250 break;
251 case COMPUTE_PKG_SIZE_DONE:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800252 if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
253 if(pkgName == null) {
254 Log.w(TAG, "Ignoring message");
255 break;
256 }
257 ps = data.getParcelable(ATTR_APP_PKG_STATS);
258 if(ps == null) {
259 Log.i(TAG, "Invalid package stats for package:"+pkgName);
260 } else {
261 int pkgId = mAppInfoAdapter.getIndex(pkgName);
262 if(mComputeIndex != pkgId) {
263 //spurious call from stale observer
264 Log.w(TAG, "Stale call back from PkgSizeObserver");
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700265 break;
266 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800267 mAppInfoAdapter.updateAppSize(pkgName, ps);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700268 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800269 mComputeIndex++;
270 if (mComputeIndex < mAppInfoAdapter.getCount()) {
271 // initiate compute package size for next pkg in list
272 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
273 mComputeIndex),
274 COMPUTE_PKG_SIZE_DONE);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700275 } else {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800276 // check for added/removed packages
277 Set<String> keys = mAddRemoveMap.keySet();
278 Iterator<String> iter = keys.iterator();
279 List<String> removeList = new ArrayList<String>();
280 boolean added = false;
281 boolean removed = false;
282 while (iter.hasNext()) {
283 String key = iter.next();
284 if (mAddRemoveMap.get(key) == Boolean.TRUE) {
285 // add
286 try {
287 info = mPm.getApplicationInfo(key, 0);
288 mAppInfoAdapter.addApplicationInfo(info);
289 added = true;
290 } catch (NameNotFoundException e) {
291 Log.w(TAG, "Invalid added package:"+key+" Ignoring entry");
292 }
293 } else {
294 // remove
295 removeList.add(key);
296 removed = true;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700297 }
298 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800299 // remove uninstalled packages from list
300 if (removed) {
301 mAppInfoAdapter.removeFromList(removeList);
302 }
303 // handle newly installed packages
304 if (added) {
305 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
306 mComputeIndex),
307 COMPUTE_PKG_SIZE_DONE);
308 } else {
309 // end computation here
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800310 mComputeSizes = true;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800311 mAppInfoAdapter.sortList(mSortOrder);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800312 mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800313 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700314 }
315 break;
316 case REMOVE_PKG:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800317 if(localLOGV) Log.i(TAG, "Message REMOVE_PKG");
318 if(pkgName == null) {
319 Log.w(TAG, "Ignoring message:REMOVE_PKG for null pkgName");
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700320 break;
321 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800322 if (!mComputeSizes) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800323 Boolean currB = mAddRemoveMap.get(pkgName);
324 if (currB == null || (currB.equals(Boolean.TRUE))) {
325 mAddRemoveMap.put(pkgName, Boolean.FALSE);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700326 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800327 break;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700328 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800329 List<String> pkgList = new ArrayList<String>();
330 pkgList.add(pkgName);
331 mAppInfoAdapter.removeFromList(pkgList);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700332 break;
333 case REORDER_LIST:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800334 if(localLOGV) Log.i(TAG, "Message REORDER_LIST");
335 int menuOption = msg.arg1;
336 if((menuOption == SORT_ORDER_ALPHA) ||
337 (menuOption == SORT_ORDER_SIZE)) {
338 // Option to sort list
339 if (menuOption != mSortOrder) {
340 mSortOrder = menuOption;
341 if (localLOGV) Log.i(TAG, "Changing sort order to "+mSortOrder);
342 mAppInfoAdapter.sortList(mSortOrder);
343 }
344 } else if(menuOption != mFilterApps) {
345 // Option to filter list
346 mFilterApps = menuOption;
347 boolean ret = mAppInfoAdapter.resetAppList(mFilterApps,
348 getInstalledApps(mFilterApps));
349 if(!ret) {
350 // Reset cache
351 mAppPropCache = null;
352 mFilterApps = FILTER_APPS_ALL;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800353 mHandler.sendEmptyMessage(INIT_PKG_INFO);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800354 sendMessageToHandler(REORDER_LIST, menuOption);
355 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700356 }
357 break;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800358 case ADD_PKG_START:
359 if(localLOGV) Log.i(TAG, "Message ADD_PKG_START");
360 if(pkgName == null) {
361 Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700362 break;
363 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800364 if (!mComputeSizes) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800365 Boolean currB = mAddRemoveMap.get(pkgName);
366 if (currB == null || (currB.equals(Boolean.FALSE))) {
367 mAddRemoveMap.put(pkgName, Boolean.TRUE);
368 }
369 break;
370 }
371 try {
372 info = mPm.getApplicationInfo(pkgName, 0);
373 } catch (NameNotFoundException e) {
374 Log.w(TAG, "Couldnt find application info for:"+pkgName);
375 break;
376 }
377 mObserver.invokeGetSizeInfo(info, ADD_PKG_DONE);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700378 break;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800379 case ADD_PKG_DONE:
380 if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
381 if(pkgName == null) {
382 Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
383 break;
384 }
385 ps = data.getParcelable(ATTR_APP_PKG_STATS);
386 mAppInfoAdapter.addToList(pkgName, ps);
387 break;
388 case REFRESH_ICONS:
389 Map<String, AppInfo> iconMap = (Map<String, AppInfo>) msg.obj;
390 if(iconMap == null) {
391 Log.w(TAG, "Error loading icons for applications");
392 } else {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800393 mAppInfoAdapter.updateAppsResourceInfo(iconMap);
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800394 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800395 mLoadLabels = true;
396 mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
397 break;
398 case NEXT_LOAD_STEP:
399 if (mComputeSizes && mLoadLabels) {
400 doneLoadingData();
401 } else if (!mComputeSizes && !mLoadLabels) {
402 // Either load the package labels or initiate get size info
403 if (mSizesFirst) {
404 initComputeSizes();
405 } else {
406 initResourceThread();
407 }
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800408 } else {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800409 // Create list view from the adapter here. Wait till the sort order
410 // of list is defined. its either by label or by size. so atleast one of the
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -0800411 // first steps should be complete before creating the list
412 createListView();
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800413 if (!mComputeSizes) {
414 initComputeSizes();
415 } else if (!mLoadLabels) {
416 initResourceThread();
417 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800418 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800419 break;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700420 default:
421 break;
422 }
423 }
424 };
425
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800426 private void doneLoadingData() {
427 setProgressBarIndeterminateVisibility(false);
428 }
429
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800430 List<ApplicationInfo> getInstalledApps(int filterOption) {
431 List<ApplicationInfo> installedAppList = mPm.getInstalledApplications(
432 PackageManager.GET_UNINSTALLED_PACKAGES);
433 if (installedAppList == null) {
434 return new ArrayList<ApplicationInfo> ();
435 }
436 if (filterOption == FILTER_APPS_THIRD_PARTY) {
437 List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
438 for (ApplicationInfo appInfo : installedAppList) {
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800439 boolean flag = false;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800440 if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800441 // Updated system app
442 flag = true;
443 } else if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
444 // Non-system app
445 flag = true;
446 }
447 if (flag) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800448 appList.add(appInfo);
449 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700450 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800451 return appList;
452 } else if (filterOption == FILTER_APPS_RUNNING) {
453 List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
454 List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
455 if ((procList == null) || (procList.size() == 0)) {
456 return appList;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700457 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800458 // Retrieve running processes from ActivityManager
459 for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
460 if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
461 int size = appProcInfo.pkgList.length;
462 for (int i = 0; i < size; i++) {
463 ApplicationInfo appInfo = null;
464 try {
465 appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i],
466 PackageManager.GET_UNINSTALLED_PACKAGES);
467 } catch (NameNotFoundException e) {
468 Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]);
469 continue;
470 }
471 if(appInfo != null) {
472 appList.add(appInfo);
473 }
474 }
475 }
476 }
477 return appList;
478 } else {
479 return installedAppList;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700480 }
481 }
482
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800483 private List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesList() {
484 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
485 return am.getRunningAppProcesses();
486 }
487
488 // some initialization code used when kicking off the size computation
489 private void initAppList(int filterOption) {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800490 mComputeSizes = false;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800491 // Initialize lists
492 List<ApplicationInfo> appList = getInstalledApps(filterOption);
493 mAddRemoveMap = new TreeMap<String, Boolean>();
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -0800494 mAppInfoAdapter = new AppInfoAdapter(this, appList);
495 // register receiver
496 mReceiver.registerReceiver();
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800497 }
498
499 // Utility method to start a thread to read application labels and icons
500 private void initResourceThread() {
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800501 //load resources now
502 if(mResourceThread.isAlive()) {
503 mResourceThread.interrupt();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800504 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800505 mResourceThread.loadAllResources(mAppInfoAdapter.getAppList());
506 }
507
508 private void initComputeSizes() {
509 // initiate compute pkg sizes
510 if (localLOGV) Log.i(TAG, "Initiating compute sizes for first time");
511 if (mAppInfoAdapter.getCount() > 0) {
512 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(0),
513 COMPUTE_PKG_SIZE_DONE);
514 } else {
515 mComputeSizes = true;
516 }
517 }
518
519 private void showEmptyViewIfListEmpty() {
520 if (localLOGV) Log.i(TAG, "Checking for empty view");
521 if (mAppInfoAdapter.getCount() > 0) {
522 mEmptyView.setVisibility(View.GONE);
523 } else {
524 mEmptyView.setVisibility(View.VISIBLE);
525 }
526 }
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -0800527
528 private void createListView() {
529 dismissLoadingMsg();
530 // get list and set listeners and adapter
531 ListView lv= (ListView) findViewById(android.R.id.list);
532 lv.setAdapter(mAppInfoAdapter);
533 lv.setOnItemClickListener(this);
534 lv.setSaveEnabled(true);
535 lv.setItemsCanFocus(true);
536 lv.setOnItemClickListener(this);
537 showEmptyViewIfListEmpty();
538 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800539
540 // internal structure used to track added and deleted packages when
541 // the activity has focus
542 class AddRemoveInfo {
543 String pkgName;
544 boolean add;
545 public AddRemoveInfo(String pPkgName, boolean pAdd) {
546 pkgName = pPkgName;
547 add = pAdd;
548 }
549 }
550
551 class ResourceLoaderThread extends Thread {
552 List<ApplicationInfo> mAppList;
553
554 void loadAllResources(List<ApplicationInfo> appList) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800555 mAppList = appList;
556 start();
557 }
558
559 public void run() {
560 Map<String, AppInfo> iconMap = new HashMap<String, AppInfo>();
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800561 if(mAppList == null || mAppList.size() <= 0) {
562 Log.w(TAG, "Empty or null application list");
563 } else {
564 for (ApplicationInfo appInfo : mAppList) {
565 CharSequence appName = appInfo.loadLabel(mPm);
566 Drawable appIcon = appInfo.loadIcon(mPm);
567 iconMap.put(appInfo.packageName,
568 new AppInfo(appInfo.packageName, appName, appIcon));
569 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800570 }
571 Message msg = mHandler.obtainMessage(REFRESH_ICONS);
572 msg.obj = iconMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700573 mHandler.sendMessage(msg);
574 }
575 }
576
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800577 /* Internal class representing an application or packages displayable attributes
578 *
579 */
580 class AppInfo {
581 public String pkgName;
582 int index;
583 public CharSequence appName;
584 public Drawable appIcon;
585 public CharSequence appSize;
586 public PackageStats appStats;
587
588 public void refreshIcon(AppInfo pInfo) {
589 appName = pInfo.appName;
590 appIcon = pInfo.appIcon;
591 }
592
593 public AppInfo(String pName, CharSequence aName, Drawable aIcon) {
594 index = -1;
595 pkgName = pName;
596 appName = aName;
597 appIcon = aIcon;
598 appStats = null;
599 appSize = mComputingSizeStr;
600 }
601
602 public AppInfo(String pName, int pIndex, CharSequence aName, Drawable aIcon,
603 PackageStats ps) {
604 index = pIndex;
605 pkgName = pName;
606 appName = aName;
607 appIcon = aIcon;
608 if(ps == null) {
609 appSize = mComputingSizeStr;
610 } else {
611 appStats = ps;
612 appSize = getSizeStr();
613 }
614 }
615 public void setSize(PackageStats ps) {
616 appStats = ps;
617 if (ps != null) {
618 appSize = getSizeStr();
619 }
620 }
621 public long getTotalSize() {
622 PackageStats ps = appStats;
623 if (ps != null) {
624 return ps.cacheSize+ps.codeSize+ps.dataSize;
625 }
626 return SIZE_INVALID;
627 }
628
629 private String getSizeStr() {
630 PackageStats ps = appStats;
631 String retStr = "";
632 // insert total size information into map to display in view
633 // at this point its guaranteed that ps is not null. but checking anyway
634 if (ps != null) {
635 long size = getTotalSize();
636 if (size == SIZE_INVALID) {
637 return mInvalidSizeStr.toString();
638 }
639 return Formatter.formatFileSize(ManageApplications.this, size);
640 }
641 return retStr;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700642 }
643 }
644
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800645 // View Holder used when displaying views
646 static class AppViewHolder {
647 TextView appName;
648 ImageView appIcon;
649 TextView appSize;
650 }
651
652 /* Custom adapter implementation for the ListView
653 * This adapter maintains a map for each displayed application and its properties
654 * An index value on each AppInfo object indicates the correct position or index
655 * in the list. If the list gets updated dynamically when the user is viewing the list of
656 * applications, we need to return the correct index of position. This is done by mapping
657 * the getId methods via the package name into the internal maps and indices.
658 * The order of applications in the list is mirrored in mAppLocalList
659 */
660 class AppInfoAdapter extends BaseAdapter {
661 private Map<String, AppInfo> mAppPropMap;
662 private List<ApplicationInfo> mAppLocalList;
663 ApplicationInfo.DisplayNameComparator mAlphaComparator;
664 AppInfoComparator mSizeComparator;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700665
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800666 private AppInfo getFromCache(String packageName) {
667 if(mAppPropCache == null) {
668 return null;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700669 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800670 return mAppPropCache.get(packageName);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700671 }
672
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800673 public AppInfoAdapter(Context c, List<ApplicationInfo> appList) {
674 mAppLocalList = appList;
675 boolean useCache = false;
676 int sortOrder = SORT_ORDER_ALPHA;
677 int imax = mAppLocalList.size();
678 if(mAppPropCache != null) {
679 useCache = true;
680 // Activity has been resumed. can use the cache to populate values initially
681 mAppPropMap = mAppPropCache;
682 sortOrder = mSortOrder;
683 }
684 sortAppList(sortOrder);
685 // Recreate property map
686 mAppPropMap = new TreeMap<String, AppInfo>();
687 for (int i = 0; i < imax; i++) {
688 ApplicationInfo info = mAppLocalList.get(i);
689 AppInfo aInfo = getFromCache(info.packageName);
690 if(aInfo == null){
691 aInfo = new AppInfo(info.packageName, i,
692 info.packageName, mDefaultAppIcon, null);
693 } else {
694 aInfo.index = i;
695 }
696 mAppPropMap.put(info.packageName, aInfo);
697 }
698 }
699
700 public int getCount() {
701 return mAppLocalList.size();
702 }
703
704 public Object getItem(int position) {
705 return mAppLocalList.get(position);
706 }
707
708 /*
709 * This method returns the index of the package position in the application list
710 */
711 public int getIndex(String pkgName) {
712 if(pkgName == null) {
713 Log.w(TAG, "Getting index of null package in List Adapter");
714 }
715 int imax = mAppLocalList.size();
716 ApplicationInfo appInfo;
717 for(int i = 0; i < imax; i++) {
718 appInfo = mAppLocalList.get(i);
719 if(appInfo.packageName.equalsIgnoreCase(pkgName)) {
720 return i;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700721 }
722 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800723 return -1;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700724 }
725
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800726 public ApplicationInfo getApplicationInfo(int position) {
727 int imax = mAppLocalList.size();
728 if( (position < 0) || (position >= imax)) {
729 Log.w(TAG, "Position out of bounds in List Adapter");
730 return null;
731 }
732 return mAppLocalList.get(position);
733 }
734
735 public void addApplicationInfo(ApplicationInfo info) {
736 if(info == null) {
737 Log.w(TAG, "Ignoring null add in List Adapter");
738 return;
739 }
740 mAppLocalList.add(info);
741 }
742
743 public long getItemId(int position) {
744 int imax = mAppLocalList.size();
745 if( (position < 0) || (position >= imax)) {
746 Log.w(TAG, "Position out of bounds in List Adapter");
747 return -1;
748 }
749 return mAppPropMap.get(mAppLocalList.get(position).packageName).index;
750 }
751
752 public List<ApplicationInfo> getAppList() {
753 return mAppLocalList;
754 }
755
756 public View getView(int position, View convertView, ViewGroup parent) {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800757 if (position >= mAppLocalList.size()) {
758 Log.w(TAG, "Invalid view position:"+position+", actual size is:"+mAppLocalList.size());
759 return null;
760 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800761 // A ViewHolder keeps references to children views to avoid unneccessary calls
762 // to findViewById() on each row.
763 AppViewHolder holder;
764
765 // When convertView is not null, we can reuse it directly, there is no need
766 // to reinflate it. We only inflate a new View when the convertView supplied
767 // by ListView is null.
768 if (convertView == null) {
769 convertView = mInflater.inflate(R.layout.manage_applications_item, null);
770
771 // Creates a ViewHolder and store references to the two children views
772 // we want to bind data to.
773 holder = new AppViewHolder();
774 holder.appName = (TextView) convertView.findViewById(R.id.app_name);
775 holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
776 holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
777 convertView.setTag(holder);
778 } else {
779 // Get the ViewHolder back to get fast access to the TextView
780 // and the ImageView.
781 holder = (AppViewHolder) convertView.getTag();
782 }
783
784 // Bind the data efficiently with the holder
785 ApplicationInfo appInfo = mAppLocalList.get(position);
786 AppInfo mInfo = mAppPropMap.get(appInfo.packageName);
787 if(mInfo != null) {
788 if(mInfo.appName != null) {
789 holder.appName.setText(mInfo.appName);
790 }
791 if(mInfo.appIcon != null) {
792 holder.appIcon.setImageDrawable(mInfo.appIcon);
793 }
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800794 if (mInfo.appSize != null) {
795 holder.appSize.setText(mInfo.appSize);
796 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800797 } else {
798 Log.w(TAG, "No info for package:"+appInfo.packageName+" in property map");
799 }
800 return convertView;
801 }
802
803 private void adjustIndex() {
804 int imax = mAppLocalList.size();
805 ApplicationInfo info;
806 for (int i = 0; i < imax; i++) {
807 info = mAppLocalList.get(i);
808 mAppPropMap.get(info.packageName).index = i;
809 }
810 }
811
812 public void sortAppList(int sortOrder) {
813 Collections.sort(mAppLocalList, getAppComparator(sortOrder));
814 }
815
816 public void sortList(int sortOrder) {
817 sortAppList(sortOrder);
818 adjustIndex();
819 notifyDataSetChanged();
820 }
821
822 public boolean resetAppList(int filterOption, List<ApplicationInfo> appList) {
823 // Create application list based on the filter value
824 mAppLocalList = appList;
825 // Check for all properties in map before sorting. Populate values from cache
826 for(ApplicationInfo applicationInfo : mAppLocalList) {
827 AppInfo appInfo = mAppPropMap.get(applicationInfo.packageName);
828 if(appInfo == null) {
829 AppInfo rInfo = getFromCache(applicationInfo.packageName);
830 if(rInfo == null) {
831 // Need to load resources again. Inconsistency somewhere
832 return false;
833 }
834 mAppPropMap.put(applicationInfo.packageName, rInfo);
835 }
836 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800837 if (mAppLocalList.size() > 0) {
838 sortList(mSortOrder);
839 } else {
840 notifyDataSetChanged();
841 }
842 showEmptyViewIfListEmpty();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800843 return true;
844 }
845
846 private Comparator<ApplicationInfo> getAppComparator(int sortOrder) {
847 if (sortOrder == SORT_ORDER_ALPHA) {
848 // Lazy initialization
849 if (mAlphaComparator == null) {
850 mAlphaComparator = new ApplicationInfo.DisplayNameComparator(mPm);
851 }
852 return mAlphaComparator;
853 }
854 // Lazy initialization
855 if(mSizeComparator == null) {
856 mSizeComparator = new AppInfoComparator(mAppPropMap);
857 }
858 return mSizeComparator;
859 }
860
861 public void updateAppsResourceInfo(Map<String, AppInfo> iconMap) {
862 if(iconMap == null) {
863 Log.w(TAG, "Null iconMap when refreshing icon in List Adapter");
864 return;
865 }
866 boolean changed = false;
867 for (ApplicationInfo info : mAppLocalList) {
868 AppInfo pInfo = iconMap.get(info.packageName);
869 if(pInfo != null) {
870 AppInfo aInfo = mAppPropMap.get(info.packageName);
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -0800871 aInfo.refreshIcon(pInfo);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800872 changed = true;
873 }
874 }
875 if(changed) {
876 notifyDataSetChanged();
877 }
878 }
879
880 public void addToList(String pkgName, PackageStats ps) {
881 if(pkgName == null) {
882 Log.w(TAG, "Adding null pkg to List Adapter");
883 return;
884 }
885 ApplicationInfo info;
886 try {
887 info = mPm.getApplicationInfo(pkgName, 0);
888 } catch (NameNotFoundException e) {
889 Log.w(TAG, "Ignoring non-existent package:"+pkgName);
890 return;
891 }
892 if(info == null) {
893 // Nothing to do log error message and return
894 Log.i(TAG, "Null ApplicationInfo for package:"+pkgName);
895 return;
896 }
897 // Binary search returns a negative index (ie --index) of the position where
898 // this might be inserted.
899 int newIdx = Collections.binarySearch(mAppLocalList, info,
900 getAppComparator(mSortOrder));
901 if(newIdx >= 0) {
902 Log.i(TAG, "Strange. Package:"+pkgName+" is not new");
903 return;
904 }
905 // New entry
906 newIdx = -newIdx-1;
907 mAppLocalList.add(newIdx, info);
908 mAppPropMap.put(info.packageName, new AppInfo(pkgName, newIdx,
909 info.loadLabel(mPm), info.loadIcon(mPm), ps));
910 adjustIndex();
911 notifyDataSetChanged();
912 }
913
914 public void removeFromList(List<String> pkgNames) {
915 if(pkgNames == null) {
916 Log.w(TAG, "Removing null pkg list from List Adapter");
917 return;
918 }
919 int imax = mAppLocalList.size();
920 boolean found = false;
921 ApplicationInfo info;
922 int i, k;
923 String pkgName;
924 int kmax = pkgNames.size();
925 if(kmax <= 0) {
926 Log.w(TAG, "Removing empty pkg list from List Adapter");
927 return;
928 }
929 int idxArr[] = new int[kmax];
930 for (k = 0; k < kmax; k++) {
931 idxArr[k] = -1;
932 }
933 for (i = 0; i < imax; i++) {
934 info = mAppLocalList.get(i);
935 for (k = 0; k < kmax; k++) {
936 pkgName = pkgNames.get(k);
937 if (info.packageName.equalsIgnoreCase(pkgName)) {
938 idxArr[k] = i;
939 found = true;
940 break;
941 }
942 }
943 }
944 // Sort idxArr
945 Arrays.sort(idxArr);
946 // remove the packages based on decending indices
947 for (k = kmax-1; k >= 0; k--) {
948 // Check if package has been found in the list of existing apps first
949 if(idxArr[k] == -1) {
950 break;
951 }
952 info = mAppLocalList.get(idxArr[k]);
953 mAppLocalList.remove(idxArr[k]);
954 mAppPropMap.remove(info.packageName);
955 if (localLOGV) Log.i(TAG, "Removed pkg:"+info.packageName+ " list");
956 }
957 if (found) {
958 adjustIndex();
959 notifyDataSetChanged();
960 }
961 }
962
963 public void updateAppSize(String pkgName, PackageStats ps) {
964 if(pkgName == null) {
965 return;
966 }
967 AppInfo entry = mAppPropMap.get(pkgName);
968 if (entry == null) {
969 Log.w(TAG, "Entry for package:"+pkgName+"doesnt exist in map");
970 return;
971 }
972 // Copy the index into the newly updated entry
973 entry.setSize(ps);
974 notifyDataSetChanged();
975 }
976
977 public PackageStats getAppStats(String pkgName) {
978 if(pkgName == null) {
979 return null;
980 }
981 AppInfo entry = mAppPropMap.get(pkgName);
982 if (entry == null) {
983 return null;
984 }
985 return entry.appStats;
986 }
987 }
988
989 /*
990 * Utility method to clear messages to Handler
991 * We need'nt synchronize on the Handler since posting messages is guaranteed
992 * to be thread safe. Even if the other thread that retrieves package sizes
993 * posts a message, we do a cursory check of validity on mAppInfoAdapter's applist
994 */
995 private void clearMessagesInHandler() {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800996 mHandler.removeMessages(INIT_PKG_INFO);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800997 mHandler.removeMessages(COMPUTE_PKG_SIZE_DONE);
998 mHandler.removeMessages(REMOVE_PKG);
999 mHandler.removeMessages(REORDER_LIST);
1000 mHandler.removeMessages(ADD_PKG_START);
1001 mHandler.removeMessages(ADD_PKG_DONE);
1002 }
1003
1004 private void sendMessageToHandler(int msgId, int arg1) {
1005 Message msg = mHandler.obtainMessage(msgId);
1006 msg.arg1 = arg1;
1007 mHandler.sendMessage(msg);
1008 }
1009
1010 private void sendMessageToHandler(int msgId, Bundle data) {
1011 Message msg = mHandler.obtainMessage(msgId);
1012 msg.setData(data);
1013 mHandler.sendMessage(msg);
1014 }
1015
1016 private void sendMessageToHandler(int msgId) {
1017 mHandler.sendEmptyMessage(msgId);
1018 }
1019
1020 /*
1021 * Stats Observer class used to compute package sizes and retrieve size information
1022 * PkgSizeOberver is the call back thats used when invoking getPackageSizeInfo on
1023 * PackageManager. The values in call back onGetStatsCompleted are validated
1024 * and the specified message is passed to mHandler. The package name
1025 * and the AppInfo object corresponding to the package name are set on the message
1026 */
1027 class PkgSizeObserver extends IPackageStatsObserver.Stub {
1028 private ApplicationInfo mAppInfo;
1029 private int mMsgId;
1030 public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) {
1031 if(DEBUG_PKG_DELAY) {
1032 try {
1033 Thread.sleep(10*1000);
1034 } catch (InterruptedException e) {
1035 }
1036 }
1037 AppInfo appInfo = null;
1038 Bundle data = new Bundle();
1039 data.putString(ATTR_PKG_NAME, mAppInfo.packageName);
1040 if(pSucceeded && pStats != null) {
1041 if (localLOGV) Log.i(TAG, "onGetStatsCompleted::"+pStats.packageName+", ("+
1042 pStats.cacheSize+","+
1043 pStats.codeSize+", "+pStats.dataSize);
1044 data.putParcelable(ATTR_APP_PKG_STATS, pStats);
1045 } else {
1046 Log.w(TAG, "Invalid package stats from PackageManager");
1047 }
1048 //post message to Handler
1049 Message msg = mHandler.obtainMessage(mMsgId, data);
1050 msg.setData(data);
1051 mHandler.sendMessage(msg);
1052 }
1053
1054 public void invokeGetSizeInfo(ApplicationInfo pAppInfo, int msgId) {
1055 if(pAppInfo == null || pAppInfo.packageName == null) {
1056 return;
1057 }
1058 if(localLOGV) Log.i(TAG, "Invoking getPackageSizeInfo for package:"+
1059 pAppInfo.packageName);
1060 mMsgId = msgId;
1061 mAppInfo = pAppInfo;
1062 mPm.getPackageSizeInfo(pAppInfo.packageName, this);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001063 }
1064 }
1065
1066 /**
1067 * Receives notifications when applications are added/removed.
1068 */
1069 private class PackageIntentReceiver extends BroadcastReceiver {
1070 void registerReceiver() {
1071 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1072 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1073 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1074 filter.addDataScheme("package");
1075 ManageApplications.this.registerReceiver(this, filter);
1076 }
1077 @Override
1078 public void onReceive(Context context, Intent intent) {
1079 String actionStr = intent.getAction();
1080 Uri data = intent.getData();
1081 String pkgName = data.getEncodedSchemeSpecificPart();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001082 if (localLOGV) Log.i(TAG, "action:"+actionStr+", for package:"+pkgName);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001083 updatePackageList(actionStr, pkgName);
1084 }
1085 }
1086
1087 private void updatePackageList(String actionStr, String pkgName) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001088 // technically we dont have to invoke handler since onReceive is invoked on
1089 // the main thread but doing it here for better clarity
1090 if (Intent.ACTION_PACKAGE_ADDED.equalsIgnoreCase(actionStr)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001091 Bundle data = new Bundle();
1092 data.putString(ATTR_PKG_NAME, pkgName);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001093 sendMessageToHandler(ADD_PKG_START, data);
1094 } else if (Intent.ACTION_PACKAGE_REMOVED.equalsIgnoreCase(actionStr)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001095 Bundle data = new Bundle();
1096 data.putString(ATTR_PKG_NAME, pkgName);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001097 sendMessageToHandler(REMOVE_PKG, data);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001098 }
1099 }
1100
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001101
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001102 @Override
1103 protected void onCreate(Bundle savedInstanceState) {
1104 super.onCreate(savedInstanceState);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001105 Intent lIntent = getIntent();
1106 String action = lIntent.getAction();
1107 if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001108 mSortOrder = SORT_ORDER_SIZE;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001109 mSizesFirst = true;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001110 }
1111 mPm = getPackageManager();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001112 // initialize some window features
1113 requestWindowFeature(Window.FEATURE_RIGHT_ICON);
1114 requestWindowFeature(Window.FEATURE_PROGRESS);
1115 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001116 setContentView(R.layout.compute_sizes);
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001117 // init mLoadingDlg
1118 mLoadingDlg = new ProgressDialog(this);
1119 mLoadingDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
1120 mLoadingDlg.setMessage(getText(R.string.loading));
1121 mLoadingDlg.setIndeterminate(true);
1122 mLoadingDlg.setOnCancelListener(this);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001123 mDefaultAppIcon =Resources.getSystem().getDrawable(
1124 com.android.internal.R.drawable.sym_def_app_icon);
1125 mInvalidSizeStr = getText(R.string.invalid_size_value);
1126 mComputingSizeStr = getText(R.string.computing_size);
1127 // initialize the inflater
1128 mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001129 mReceiver = new PackageIntentReceiver();
1130 mEmptyView = (TextView) findViewById(R.id.empty_view);
1131 mObserver = new PkgSizeObserver();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001132 }
1133
The Android Open Source Project8a156092009-03-02 22:54:43 -08001134 private void showLoadingMsg() {
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001135 if (mLoadingDlg != null) {
1136 if(localLOGV) Log.i(TAG, "Displaying Loading message");
1137 mLoadingDlg.show();
1138 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001139 }
1140
1141 private void dismissLoadingMsg() {
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001142 if ((mLoadingDlg != null) && (mLoadingDlg.isShowing())) {
1143 if(localLOGV) Log.i(TAG, "Dismissing Loading message");
1144 mLoadingDlg.dismiss();
1145 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001146 }
1147
1148 @Override
1149 public void onStart() {
1150 super.onStart();
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001151 showLoadingMsg();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001152 // Create a thread to load resources
1153 mResourceThread = new ResourceLoaderThread();
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001154 sendMessageToHandler(INIT_PKG_INFO);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001155 }
1156
1157 @Override
1158 public void onStop() {
1159 super.onStop();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001160 // clear all messages related to application list
1161 clearMessagesInHandler();
1162 // register receiver here
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001163 unregisterReceiver(mReceiver);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001164 mAppPropCache = mAppInfoAdapter.mAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001165 }
1166
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001167 /*
1168 * comparator class used to sort AppInfo objects based on size
1169 */
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001170 public static class AppInfoComparator implements Comparator<ApplicationInfo> {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001171 public AppInfoComparator(Map<String, AppInfo> pAppPropMap) {
1172 mAppPropMap= pAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001173 }
1174
1175 public final int compare(ApplicationInfo a, ApplicationInfo b) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001176 AppInfo ainfo = mAppPropMap.get(a.packageName);
1177 AppInfo binfo = mAppPropMap.get(b.packageName);
1178 long atotal = ainfo.getTotalSize();
1179 long btotal = binfo.getTotalSize();
1180 long ret = atotal - btotal;
1181 // negate result to sort in descending order
1182 if (ret < 0) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001183 return 1;
1184 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001185 if (ret == 0) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001186 return 0;
1187 }
1188 return -1;
1189 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001190 private Map<String, AppInfo> mAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001191 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001192
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001193 // utility method used to start sub activity
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001194 private void startApplicationDetailsActivity(ApplicationInfo info, PackageStats ps) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001195 // Create intent to start new activity
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001196 Intent intent = new Intent(Intent.ACTION_VIEW);
1197 intent.setClass(this, InstalledAppDetails.class);
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001198 mCurrentPkgName = info.packageName;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001199 intent.putExtra(APP_PKG_NAME, mCurrentPkgName);
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001200 intent.putExtra(APP_PKG_SIZE, ps);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001201 // start new activity to display extended information
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001202 startActivityForResult(intent, INSTALLED_APP_DETAILS);
1203 }
1204
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001205 @Override
1206 public boolean onCreateOptionsMenu(Menu menu) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001207 menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha)
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001208 .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001209 menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
1210 .setIcon(android.R.drawable.ic_menu_sort_by_size);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001211 menu.add(0, FILTER_OPTIONS, 3, R.string.filter)
1212 .setIcon(R.drawable.ic_menu_filter_settings);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001213 return true;
1214 }
1215
1216 @Override
1217 public boolean onPrepareOptionsMenu(Menu menu) {
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001218 if (mComputeSizes) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001219 menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001220 menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001221 menu.findItem(FILTER_OPTIONS).setVisible(true);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001222 return true;
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001223 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001224 return false;
1225 }
1226
1227 @Override
1228 public boolean onOptionsItemSelected(MenuItem item) {
1229 int menuId = item.getItemId();
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001230 if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
1231 sendMessageToHandler(REORDER_LIST, menuId);
1232 } else if (menuId == FILTER_OPTIONS) {
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001233 if (mAlertDlgBuilder == null) {
1234 mAlertDlgBuilder = new AlertDialog.Builder(this).
1235 setTitle(R.string.filter_dlg_title).
1236 setNeutralButton(R.string.cancel, this).
1237 setSingleChoiceItems(new CharSequence[] {getText(R.string.filter_apps_all),
1238 getText(R.string.filter_apps_running),
1239 getText(R.string.filter_apps_third_party)},
1240 -1, this);
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001241 }
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001242 mAlertDlgBuilder.show();
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001243 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001244 return true;
1245 }
1246
1247 public void onItemClick(AdapterView<?> parent, View view, int position,
1248 long id) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001249 ApplicationInfo info = (ApplicationInfo)mAppInfoAdapter.getItem(position);
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001250 startApplicationDetailsActivity(info, mAppInfoAdapter.getAppStats(info.packageName));
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001251 }
1252
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001253 // onCancel call back for dialog thats displayed when data is being loaded
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001254 public void onCancel(DialogInterface dialog) {
The Android Open Source Projecta578a6c2009-03-03 14:04:35 -08001255 mLoadingDlg = null;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001256 finish();
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001257 }
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001258
1259 public void onClick(DialogInterface dialog, int which) {
1260 int newOption;
1261 switch (which) {
1262 // Make sure that values of 0, 1, 2 match options all, running, third_party when
1263 // created via the AlertDialog.Builder
1264 case 0:
1265 newOption = FILTER_APPS_ALL;
1266 break;
1267 case 1:
1268 newOption = FILTER_APPS_RUNNING;
1269 break;
1270 case 2:
1271 newOption = FILTER_APPS_THIRD_PARTY;
1272 break;
1273 default:
1274 return;
1275 }
1276 sendMessageToHandler(REORDER_LIST, newOption);
1277 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001278}