blob: 74957ed430ff159dccc6daace74cd0ba3f5e7554 [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 Project8a156092009-03-02 22:54:43 -080022import android.app.Dialog;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080023import android.app.ListActivity;
24import android.app.ProgressDialog;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070025import android.content.BroadcastReceiver;
26import android.content.Context;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080027import android.content.DialogInterface;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070028import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.pm.ApplicationInfo;
31import android.content.pm.IPackageStatsObserver;
32import android.content.pm.PackageManager;
33import android.content.pm.PackageStats;
34import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Project8a156092009-03-02 22:54:43 -080035import android.content.res.Configuration;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080036import android.content.res.Resources;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070037import android.graphics.drawable.Drawable;
38import android.net.Uri;
39import android.os.Bundle;
40import android.os.Handler;
41import android.os.Message;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080042import android.text.format.Formatter;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070043import android.util.Config;
44import android.util.Log;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080045import android.view.LayoutInflater;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070046import android.view.Menu;
47import android.view.MenuItem;
48import android.view.View;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080049import android.view.ViewGroup;
50import android.view.Window;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070051import android.widget.AdapterView;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080052import android.widget.BaseAdapter;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070053import android.widget.ImageView;
54import android.widget.ListView;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070055import android.widget.TextView;
56import android.widget.AdapterView.OnItemClickListener;
57import java.util.ArrayList;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080058import java.util.Arrays;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070059import java.util.Collections;
60import java.util.Comparator;
61import java.util.HashMap;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080062import java.util.Iterator;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070063import java.util.List;
64import java.util.Map;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080065import java.util.Set;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070066import java.util.TreeMap;
67
68/**
69 * Activity to pick an application that will be used to display installation information and
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080070 * options to uninstall/delete user data for system applications. This activity
71 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
72 * intent.
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -070073 * Initially a compute in progress message is displayed while the application retrieves
The Android Open Source Projectabc48f82008-12-17 18:06:01 -080074 * the list of application information from the PackageManager. The size information
75 * for each package is refreshed to the screen. The resource(app description and
76 * icon) information for each package is not available yet, so some default values for size
77 * icon and descriptions are used initially. Later the resource information for each
78 * application is retrieved and dynamically updated on the screen.
79 * A Broadcast receiver registers for package additions or deletions when the activity is
80 * in focus. If the user installs or deletes packages when the activity has focus, the receiver
81 * gets notified and proceeds to add/delete these packages from the list on the screen.
82 * This is an unlikely scenario but could happen. The entire list gets created every time
83 * the activity's onStart gets invoked. This is to avoid having the receiver for the entire
84 * life cycle of the application.
85 * The applications can be sorted either alphabetically or
86 * based on size(descending). If this activity gets launched under low memory
87 * situations(A low memory notification dispatches intent
88 * ACTION_MANAGE_PACKAGE_STORAGE) the list is sorted per size.
89 * If the user selects an application, extended info(like size, uninstall/clear data options,
90 * permissions info etc.,) is displayed via the InstalledAppDetails activity.
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 Project8a156092009-03-02 22:54:43 -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 Projectabc48f82008-12-17 18:06:01 -0800104 public static final String APP_CHG = APP_PKG_PREFIX+"changed";
105
106 // attribute name used in receiver for tagging names of added/deleted packages
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700107 private static final String ATTR_PKG_NAME="PackageName";
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800108 private static final String ATTR_APP_PKG_STATS="ApplicationPackageStats";
109
110 // constant value that can be used to check return code from sub activity.
111 private static final int INSTALLED_APP_DETAILS = 1;
112
113 // sort order that can be changed through the menu can be sorted alphabetically
114 // or size(descending)
115 private static final int MENU_OPTIONS_BASE = 0;
116 public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 0;
117 public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 1;
118 // Filter options used for displayed list of applications
119 public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 2;
120 public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 3;
121 public static final int FILTER_APPS_RUNNING = MENU_OPTIONS_BASE + 4;
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800122 public static final int FILTER_OPTIONS = MENU_OPTIONS_BASE + 5;
123 // Alert Dialog presented to user to find out the filter option
The Android Open Source Project8a156092009-03-02 22:54:43 -0800124 AlertDialog mAlertDlg;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800125 // sort order
126 private int mSortOrder = SORT_ORDER_ALPHA;
127 // Filter value
128 int mFilterApps = FILTER_APPS_ALL;
129
130 // Custom Adapter used for managing items in the list
131 private AppInfoAdapter mAppInfoAdapter;
132
133 // messages posted to the handler
134 private static final int HANDLER_MESSAGE_BASE = 0;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800135 private static final int INIT_PKG_INFO = HANDLER_MESSAGE_BASE+1;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800136 private static final int COMPUTE_PKG_SIZE_DONE = HANDLER_MESSAGE_BASE+2;
137 private static final int REMOVE_PKG = HANDLER_MESSAGE_BASE+3;
138 private static final int REORDER_LIST = HANDLER_MESSAGE_BASE+4;
139 private static final int ADD_PKG_START = HANDLER_MESSAGE_BASE+5;
140 private static final int ADD_PKG_DONE = HANDLER_MESSAGE_BASE+6;
141 private static final int REFRESH_ICONS = HANDLER_MESSAGE_BASE+7;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800142 private static final int NEXT_LOAD_STEP = HANDLER_MESSAGE_BASE+8;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800143
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 Projectb9f58512009-02-13 12:57:53 -0800153 private boolean mComputeSizes = 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
The Android Open Source Project8a156092009-03-02 22:54:43 -0800159 private static final int DLG_BASE = 0;
160 private static final int DLG_LOADING = DLG_BASE + 1;
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;
The Android Open Source Project8a156092009-03-02 22:54:43 -0800199 // ListView used to display list
200 private ListView mListView;
201 // State variables used to figure out menu options and also
202 // initiate the first computation and loading of resources
203 private boolean mJustCreated = true;
204 private boolean mFirst = false;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800205
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700206 /*
207 * Handler class to handle messages for various operations
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800208 * Most of the operations that effect Application related data
209 * are posted as messages to the handler to avoid synchronization
210 * when accessing these structures.
211 * When the size retrieval gets kicked off for the first time, a COMPUTE_PKG_SIZE_START
212 * message is posted to the handler which invokes the getSizeInfo for the pkg at index 0
213 * When the PackageManager's asynchronous call back through
214 * PkgSizeObserver.onGetStatsCompleted gets invoked, the application resources like
215 * label, description, icon etc., is loaded in the same thread and these values are
216 * set on the observer. The observer then posts a COMPUTE_PKG_SIZE_DONE message
217 * to the handler. This information is updated on the AppInfoAdapter associated with
218 * the list view of this activity and size info retrieval is initiated for the next package as
219 * indicated by mComputeIndex
220 * When a package gets added while the activity has focus, the PkgSizeObserver posts
221 * ADD_PKG_START message to the handler. If the computation is not in progress, the size
222 * is retrieved for the newly added package through the observer object and the newly
223 * installed app info is updated on the screen. If the computation is still in progress
224 * the package is added to an internal structure and action deferred till the computation
225 * is done for all the packages.
226 * When a package gets deleted, REMOVE_PKG is posted to the handler
227 * if computation is not in progress(as indicated by
228 * mDoneIniting), the package is deleted from the displayed list of apps. If computation is
229 * still in progress the package is added to an internal structure and action deferred till
230 * the computation is done for all packages.
231 * When the sizes of all packages is computed, the newly
232 * added or removed packages are processed in order.
233 * If the user changes the order in which these applications are viewed by hitting the
234 * menu key, REORDER_LIST message is posted to the handler. this sorts the list
235 * of items based on the sort order.
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700236 */
237 private Handler mHandler = new Handler() {
238 public void handleMessage(Message msg) {
239 PackageStats ps;
240 ApplicationInfo info;
241 Bundle data;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800242 String pkgName = null;
243 AppInfo appInfo;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700244 data = msg.getData();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800245 if(data != null) {
246 pkgName = data.getString(ATTR_PKG_NAME);
247 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700248 switch (msg.what) {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800249 case INIT_PKG_INFO:
250 if(localLOGV) Log.i(TAG, "Message INIT_PKG_INFO");
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800251 // Retrieve the package list and init some structures
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800252 initAppList(mFilterApps);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800253 mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700254 break;
255 case COMPUTE_PKG_SIZE_DONE:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800256 if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
257 if(pkgName == null) {
258 Log.w(TAG, "Ignoring message");
259 break;
260 }
261 ps = data.getParcelable(ATTR_APP_PKG_STATS);
262 if(ps == null) {
263 Log.i(TAG, "Invalid package stats for package:"+pkgName);
264 } else {
265 int pkgId = mAppInfoAdapter.getIndex(pkgName);
266 if(mComputeIndex != pkgId) {
267 //spurious call from stale observer
268 Log.w(TAG, "Stale call back from PkgSizeObserver");
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700269 break;
270 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800271 mAppInfoAdapter.updateAppSize(pkgName, ps);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700272 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800273 mComputeIndex++;
274 if (mComputeIndex < mAppInfoAdapter.getCount()) {
275 // initiate compute package size for next pkg in list
276 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
277 mComputeIndex),
278 COMPUTE_PKG_SIZE_DONE);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700279 } else {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800280 // check for added/removed packages
281 Set<String> keys = mAddRemoveMap.keySet();
282 Iterator<String> iter = keys.iterator();
283 List<String> removeList = new ArrayList<String>();
284 boolean added = false;
285 boolean removed = false;
286 while (iter.hasNext()) {
287 String key = iter.next();
288 if (mAddRemoveMap.get(key) == Boolean.TRUE) {
289 // add
290 try {
291 info = mPm.getApplicationInfo(key, 0);
292 mAppInfoAdapter.addApplicationInfo(info);
293 added = true;
294 } catch (NameNotFoundException e) {
295 Log.w(TAG, "Invalid added package:"+key+" Ignoring entry");
296 }
297 } else {
298 // remove
299 removeList.add(key);
300 removed = true;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700301 }
302 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800303 // remove uninstalled packages from list
304 if (removed) {
305 mAppInfoAdapter.removeFromList(removeList);
306 }
307 // handle newly installed packages
308 if (added) {
309 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(
310 mComputeIndex),
311 COMPUTE_PKG_SIZE_DONE);
312 } else {
313 // end computation here
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800314 mComputeSizes = true;
The Android Open Source Project8a156092009-03-02 22:54:43 -0800315 mFirst = true;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800316 mAppInfoAdapter.sortList(mSortOrder);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800317 mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800318 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700319 }
320 break;
321 case REMOVE_PKG:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800322 if(localLOGV) Log.i(TAG, "Message REMOVE_PKG");
323 if(pkgName == null) {
324 Log.w(TAG, "Ignoring message:REMOVE_PKG for null pkgName");
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700325 break;
326 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800327 if (!mComputeSizes) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800328 Boolean currB = mAddRemoveMap.get(pkgName);
329 if (currB == null || (currB.equals(Boolean.TRUE))) {
330 mAddRemoveMap.put(pkgName, Boolean.FALSE);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700331 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800332 break;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700333 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800334 List<String> pkgList = new ArrayList<String>();
335 pkgList.add(pkgName);
336 mAppInfoAdapter.removeFromList(pkgList);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700337 break;
338 case REORDER_LIST:
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800339 if(localLOGV) Log.i(TAG, "Message REORDER_LIST");
340 int menuOption = msg.arg1;
341 if((menuOption == SORT_ORDER_ALPHA) ||
342 (menuOption == SORT_ORDER_SIZE)) {
343 // Option to sort list
344 if (menuOption != mSortOrder) {
345 mSortOrder = menuOption;
346 if (localLOGV) Log.i(TAG, "Changing sort order to "+mSortOrder);
347 mAppInfoAdapter.sortList(mSortOrder);
348 }
349 } else if(menuOption != mFilterApps) {
350 // Option to filter list
351 mFilterApps = menuOption;
352 boolean ret = mAppInfoAdapter.resetAppList(mFilterApps,
353 getInstalledApps(mFilterApps));
354 if(!ret) {
355 // Reset cache
356 mAppPropCache = null;
357 mFilterApps = FILTER_APPS_ALL;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800358 mHandler.sendEmptyMessage(INIT_PKG_INFO);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800359 sendMessageToHandler(REORDER_LIST, menuOption);
360 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700361 }
362 break;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800363 case ADD_PKG_START:
364 if(localLOGV) Log.i(TAG, "Message ADD_PKG_START");
365 if(pkgName == null) {
366 Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700367 break;
368 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800369 if (!mComputeSizes) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800370 Boolean currB = mAddRemoveMap.get(pkgName);
371 if (currB == null || (currB.equals(Boolean.FALSE))) {
372 mAddRemoveMap.put(pkgName, Boolean.TRUE);
373 }
374 break;
375 }
376 try {
377 info = mPm.getApplicationInfo(pkgName, 0);
378 } catch (NameNotFoundException e) {
379 Log.w(TAG, "Couldnt find application info for:"+pkgName);
380 break;
381 }
382 mObserver.invokeGetSizeInfo(info, ADD_PKG_DONE);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700383 break;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800384 case ADD_PKG_DONE:
385 if(localLOGV) Log.i(TAG, "Message COMPUTE_PKG_SIZE_DONE");
386 if(pkgName == null) {
387 Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName");
388 break;
389 }
390 ps = data.getParcelable(ATTR_APP_PKG_STATS);
391 mAppInfoAdapter.addToList(pkgName, ps);
392 break;
393 case REFRESH_ICONS:
394 Map<String, AppInfo> iconMap = (Map<String, AppInfo>) msg.obj;
395 if(iconMap == null) {
396 Log.w(TAG, "Error loading icons for applications");
397 } else {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800398 mAppInfoAdapter.updateAppsResourceInfo(iconMap);
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800399 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800400 mLoadLabels = true;
401 mHandler.sendEmptyMessage(NEXT_LOAD_STEP);
402 break;
403 case NEXT_LOAD_STEP:
404 if (mComputeSizes && mLoadLabels) {
405 doneLoadingData();
406 } else if (!mComputeSizes && !mLoadLabels) {
407 // Either load the package labels or initiate get size info
408 if (mSizesFirst) {
409 initComputeSizes();
410 } else {
411 initResourceThread();
412 }
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800413 } else {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800414 // Create list view from the adapter here. Wait till the sort order
415 // of list is defined. its either by label or by size. so atleast one of the
The Android Open Source Project8a156092009-03-02 22:54:43 -0800416 // first steps should be complete before filling the list
417 if (mJustCreated) {
418 // Set the adapter here.
419 mJustCreated = false;
420 mListView.setAdapter(mAppInfoAdapter);
421 dismissLoadingMsg();
422 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800423 if (!mComputeSizes) {
424 initComputeSizes();
425 } else if (!mLoadLabels) {
426 initResourceThread();
427 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800428 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800429 break;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700430 default:
431 break;
432 }
433 }
434 };
435
The Android Open Source Project8a156092009-03-02 22:54:43 -0800436
437
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800438 private void doneLoadingData() {
439 setProgressBarIndeterminateVisibility(false);
440 }
441
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800442 List<ApplicationInfo> getInstalledApps(int filterOption) {
443 List<ApplicationInfo> installedAppList = mPm.getInstalledApplications(
444 PackageManager.GET_UNINSTALLED_PACKAGES);
445 if (installedAppList == null) {
446 return new ArrayList<ApplicationInfo> ();
447 }
448 if (filterOption == FILTER_APPS_THIRD_PARTY) {
449 List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
450 for (ApplicationInfo appInfo : installedAppList) {
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800451 boolean flag = false;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800452 if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800453 // Updated system app
454 flag = true;
455 } else if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
456 // Non-system app
457 flag = true;
458 }
459 if (flag) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800460 appList.add(appInfo);
461 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700462 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800463 return appList;
464 } else if (filterOption == FILTER_APPS_RUNNING) {
465 List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
466 List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
467 if ((procList == null) || (procList.size() == 0)) {
468 return appList;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700469 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800470 // Retrieve running processes from ActivityManager
471 for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
472 if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
473 int size = appProcInfo.pkgList.length;
474 for (int i = 0; i < size; i++) {
475 ApplicationInfo appInfo = null;
476 try {
477 appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i],
478 PackageManager.GET_UNINSTALLED_PACKAGES);
479 } catch (NameNotFoundException e) {
480 Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]);
481 continue;
482 }
483 if(appInfo != null) {
484 appList.add(appInfo);
485 }
486 }
487 }
488 }
489 return appList;
490 } else {
491 return installedAppList;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700492 }
493 }
494
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800495 private List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesList() {
496 ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
497 return am.getRunningAppProcesses();
498 }
499
500 // some initialization code used when kicking off the size computation
501 private void initAppList(int filterOption) {
The Android Open Source Project8a156092009-03-02 22:54:43 -0800502 setProgressBarIndeterminateVisibility(true);
503 mComputeIndex = 0;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800504 mComputeSizes = false;
The Android Open Source Project8a156092009-03-02 22:54:43 -0800505 mLoadLabels = false;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800506 // Initialize lists
507 List<ApplicationInfo> appList = getInstalledApps(filterOption);
508 mAddRemoveMap = new TreeMap<String, Boolean>();
The Android Open Source Project8a156092009-03-02 22:54:43 -0800509 mAppInfoAdapter.resetAppList(filterOption, appList);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800510 }
511
512 // Utility method to start a thread to read application labels and icons
513 private void initResourceThread() {
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800514 //load resources now
515 if(mResourceThread.isAlive()) {
516 mResourceThread.interrupt();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800517 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800518 mResourceThread.loadAllResources(mAppInfoAdapter.getAppList());
519 }
520
521 private void initComputeSizes() {
522 // initiate compute pkg sizes
523 if (localLOGV) Log.i(TAG, "Initiating compute sizes for first time");
524 if (mAppInfoAdapter.getCount() > 0) {
525 mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo(0),
526 COMPUTE_PKG_SIZE_DONE);
527 } else {
528 mComputeSizes = true;
529 }
530 }
531
532 private void showEmptyViewIfListEmpty() {
533 if (localLOGV) Log.i(TAG, "Checking for empty view");
534 if (mAppInfoAdapter.getCount() > 0) {
The Android Open Source Project8a156092009-03-02 22:54:43 -0800535 mListView.setVisibility(View.VISIBLE);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800536 mEmptyView.setVisibility(View.GONE);
537 } else {
The Android Open Source Project8a156092009-03-02 22:54:43 -0800538 mListView.setVisibility(View.GONE);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800539 mEmptyView.setVisibility(View.VISIBLE);
540 }
541 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800542
543 // internal structure used to track added and deleted packages when
544 // the activity has focus
545 class AddRemoveInfo {
546 String pkgName;
547 boolean add;
548 public AddRemoveInfo(String pPkgName, boolean pAdd) {
549 pkgName = pPkgName;
550 add = pAdd;
551 }
552 }
553
554 class ResourceLoaderThread extends Thread {
555 List<ApplicationInfo> mAppList;
556
557 void loadAllResources(List<ApplicationInfo> appList) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800558 mAppList = appList;
559 start();
560 }
561
562 public void run() {
563 Map<String, AppInfo> iconMap = new HashMap<String, AppInfo>();
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800564 if(mAppList == null || mAppList.size() <= 0) {
565 Log.w(TAG, "Empty or null application list");
566 } else {
567 for (ApplicationInfo appInfo : mAppList) {
568 CharSequence appName = appInfo.loadLabel(mPm);
569 Drawable appIcon = appInfo.loadIcon(mPm);
570 iconMap.put(appInfo.packageName,
571 new AppInfo(appInfo.packageName, appName, appIcon));
572 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800573 }
574 Message msg = mHandler.obtainMessage(REFRESH_ICONS);
575 msg.obj = iconMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700576 mHandler.sendMessage(msg);
577 }
578 }
579
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800580 /* Internal class representing an application or packages displayable attributes
581 *
582 */
583 class AppInfo {
584 public String pkgName;
585 int index;
586 public CharSequence appName;
587 public Drawable appIcon;
588 public CharSequence appSize;
589 public PackageStats appStats;
590
591 public void refreshIcon(AppInfo pInfo) {
592 appName = pInfo.appName;
593 appIcon = pInfo.appIcon;
594 }
595
596 public AppInfo(String pName, CharSequence aName, Drawable aIcon) {
597 index = -1;
598 pkgName = pName;
599 appName = aName;
600 appIcon = aIcon;
601 appStats = null;
602 appSize = mComputingSizeStr;
603 }
604
605 public AppInfo(String pName, int pIndex, CharSequence aName, Drawable aIcon,
606 PackageStats ps) {
607 index = pIndex;
608 pkgName = pName;
609 appName = aName;
610 appIcon = aIcon;
611 if(ps == null) {
612 appSize = mComputingSizeStr;
613 } else {
614 appStats = ps;
615 appSize = getSizeStr();
616 }
617 }
618 public void setSize(PackageStats ps) {
619 appStats = ps;
620 if (ps != null) {
621 appSize = getSizeStr();
622 }
623 }
624 public long getTotalSize() {
625 PackageStats ps = appStats;
626 if (ps != null) {
627 return ps.cacheSize+ps.codeSize+ps.dataSize;
628 }
629 return SIZE_INVALID;
630 }
631
632 private String getSizeStr() {
633 PackageStats ps = appStats;
634 String retStr = "";
635 // insert total size information into map to display in view
636 // at this point its guaranteed that ps is not null. but checking anyway
637 if (ps != null) {
638 long size = getTotalSize();
639 if (size == SIZE_INVALID) {
640 return mInvalidSizeStr.toString();
641 }
642 return Formatter.formatFileSize(ManageApplications.this, size);
643 }
644 return retStr;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700645 }
646 }
647
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800648 // View Holder used when displaying views
649 static class AppViewHolder {
650 TextView appName;
651 ImageView appIcon;
652 TextView appSize;
653 }
654
655 /* Custom adapter implementation for the ListView
656 * This adapter maintains a map for each displayed application and its properties
657 * An index value on each AppInfo object indicates the correct position or index
658 * in the list. If the list gets updated dynamically when the user is viewing the list of
659 * applications, we need to return the correct index of position. This is done by mapping
660 * the getId methods via the package name into the internal maps and indices.
661 * The order of applications in the list is mirrored in mAppLocalList
662 */
663 class AppInfoAdapter extends BaseAdapter {
664 private Map<String, AppInfo> mAppPropMap;
665 private List<ApplicationInfo> mAppLocalList;
666 ApplicationInfo.DisplayNameComparator mAlphaComparator;
667 AppInfoComparator mSizeComparator;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700668
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800669 private AppInfo getFromCache(String packageName) {
670 if(mAppPropCache == null) {
671 return null;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700672 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800673 return mAppPropCache.get(packageName);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700674 }
675
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800676 public AppInfoAdapter(Context c, List<ApplicationInfo> appList) {
677 mAppLocalList = appList;
678 boolean useCache = false;
679 int sortOrder = SORT_ORDER_ALPHA;
680 int imax = mAppLocalList.size();
681 if(mAppPropCache != null) {
682 useCache = true;
683 // Activity has been resumed. can use the cache to populate values initially
684 mAppPropMap = mAppPropCache;
685 sortOrder = mSortOrder;
686 }
687 sortAppList(sortOrder);
688 // Recreate property map
689 mAppPropMap = new TreeMap<String, AppInfo>();
690 for (int i = 0; i < imax; i++) {
691 ApplicationInfo info = mAppLocalList.get(i);
692 AppInfo aInfo = getFromCache(info.packageName);
693 if(aInfo == null){
694 aInfo = new AppInfo(info.packageName, i,
695 info.packageName, mDefaultAppIcon, null);
696 } else {
697 aInfo.index = i;
698 }
699 mAppPropMap.put(info.packageName, aInfo);
700 }
701 }
702
703 public int getCount() {
704 return mAppLocalList.size();
705 }
706
707 public Object getItem(int position) {
708 return mAppLocalList.get(position);
709 }
710
711 /*
712 * This method returns the index of the package position in the application list
713 */
714 public int getIndex(String pkgName) {
715 if(pkgName == null) {
716 Log.w(TAG, "Getting index of null package in List Adapter");
717 }
718 int imax = mAppLocalList.size();
719 ApplicationInfo appInfo;
720 for(int i = 0; i < imax; i++) {
721 appInfo = mAppLocalList.get(i);
722 if(appInfo.packageName.equalsIgnoreCase(pkgName)) {
723 return i;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700724 }
725 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800726 return -1;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700727 }
728
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800729 public ApplicationInfo getApplicationInfo(int position) {
730 int imax = mAppLocalList.size();
731 if( (position < 0) || (position >= imax)) {
732 Log.w(TAG, "Position out of bounds in List Adapter");
733 return null;
734 }
735 return mAppLocalList.get(position);
736 }
737
738 public void addApplicationInfo(ApplicationInfo info) {
739 if(info == null) {
740 Log.w(TAG, "Ignoring null add in List Adapter");
741 return;
742 }
743 mAppLocalList.add(info);
744 }
745
746 public long getItemId(int position) {
747 int imax = mAppLocalList.size();
748 if( (position < 0) || (position >= imax)) {
749 Log.w(TAG, "Position out of bounds in List Adapter");
750 return -1;
751 }
752 return mAppPropMap.get(mAppLocalList.get(position).packageName).index;
753 }
754
755 public List<ApplicationInfo> getAppList() {
756 return mAppLocalList;
757 }
758
759 public View getView(int position, View convertView, ViewGroup parent) {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800760 if (position >= mAppLocalList.size()) {
761 Log.w(TAG, "Invalid view position:"+position+", actual size is:"+mAppLocalList.size());
762 return null;
763 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800764 // A ViewHolder keeps references to children views to avoid unneccessary calls
765 // to findViewById() on each row.
766 AppViewHolder holder;
767
768 // When convertView is not null, we can reuse it directly, there is no need
769 // to reinflate it. We only inflate a new View when the convertView supplied
770 // by ListView is null.
771 if (convertView == null) {
772 convertView = mInflater.inflate(R.layout.manage_applications_item, null);
773
774 // Creates a ViewHolder and store references to the two children views
775 // we want to bind data to.
776 holder = new AppViewHolder();
777 holder.appName = (TextView) convertView.findViewById(R.id.app_name);
778 holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
779 holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
780 convertView.setTag(holder);
781 } else {
782 // Get the ViewHolder back to get fast access to the TextView
783 // and the ImageView.
784 holder = (AppViewHolder) convertView.getTag();
785 }
786
787 // Bind the data efficiently with the holder
788 ApplicationInfo appInfo = mAppLocalList.get(position);
789 AppInfo mInfo = mAppPropMap.get(appInfo.packageName);
790 if(mInfo != null) {
791 if(mInfo.appName != null) {
792 holder.appName.setText(mInfo.appName);
793 }
794 if(mInfo.appIcon != null) {
795 holder.appIcon.setImageDrawable(mInfo.appIcon);
796 }
The Android Open Source Project1feaa852009-02-10 15:44:05 -0800797 if (mInfo.appSize != null) {
798 holder.appSize.setText(mInfo.appSize);
799 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800800 } else {
801 Log.w(TAG, "No info for package:"+appInfo.packageName+" in property map");
802 }
803 return convertView;
804 }
805
806 private void adjustIndex() {
807 int imax = mAppLocalList.size();
808 ApplicationInfo info;
809 for (int i = 0; i < imax; i++) {
810 info = mAppLocalList.get(i);
811 mAppPropMap.get(info.packageName).index = i;
812 }
813 }
814
815 public void sortAppList(int sortOrder) {
816 Collections.sort(mAppLocalList, getAppComparator(sortOrder));
817 }
818
819 public void sortList(int sortOrder) {
820 sortAppList(sortOrder);
821 adjustIndex();
822 notifyDataSetChanged();
823 }
824
825 public boolean resetAppList(int filterOption, List<ApplicationInfo> appList) {
826 // Create application list based on the filter value
827 mAppLocalList = appList;
828 // Check for all properties in map before sorting. Populate values from cache
829 for(ApplicationInfo applicationInfo : mAppLocalList) {
830 AppInfo appInfo = mAppPropMap.get(applicationInfo.packageName);
831 if(appInfo == null) {
832 AppInfo rInfo = getFromCache(applicationInfo.packageName);
833 if(rInfo == null) {
834 // Need to load resources again. Inconsistency somewhere
835 return false;
836 }
837 mAppPropMap.put(applicationInfo.packageName, rInfo);
838 }
839 }
The Android Open Source Projectb9f58512009-02-13 12:57:53 -0800840 if (mAppLocalList.size() > 0) {
841 sortList(mSortOrder);
842 } else {
843 notifyDataSetChanged();
844 }
845 showEmptyViewIfListEmpty();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800846 return true;
847 }
848
849 private Comparator<ApplicationInfo> getAppComparator(int sortOrder) {
850 if (sortOrder == SORT_ORDER_ALPHA) {
851 // Lazy initialization
852 if (mAlphaComparator == null) {
853 mAlphaComparator = new ApplicationInfo.DisplayNameComparator(mPm);
854 }
855 return mAlphaComparator;
856 }
857 // Lazy initialization
858 if(mSizeComparator == null) {
859 mSizeComparator = new AppInfoComparator(mAppPropMap);
860 }
861 return mSizeComparator;
862 }
863
864 public void updateAppsResourceInfo(Map<String, AppInfo> iconMap) {
865 if(iconMap == null) {
866 Log.w(TAG, "Null iconMap when refreshing icon in List Adapter");
867 return;
868 }
869 boolean changed = false;
870 for (ApplicationInfo info : mAppLocalList) {
871 AppInfo pInfo = iconMap.get(info.packageName);
872 if(pInfo != null) {
873 AppInfo aInfo = mAppPropMap.get(info.packageName);
The Android Open Source Project8a156092009-03-02 22:54:43 -0800874 if (aInfo != null) {
875 aInfo.refreshIcon(pInfo);
876 } else {
877 mAppPropMap.put(info.packageName, pInfo);
878 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -0800879 changed = true;
880 }
881 }
882 if(changed) {
883 notifyDataSetChanged();
884 }
885 }
886
887 public void addToList(String pkgName, PackageStats ps) {
888 if(pkgName == null) {
889 Log.w(TAG, "Adding null pkg to List Adapter");
890 return;
891 }
892 ApplicationInfo info;
893 try {
894 info = mPm.getApplicationInfo(pkgName, 0);
895 } catch (NameNotFoundException e) {
896 Log.w(TAG, "Ignoring non-existent package:"+pkgName);
897 return;
898 }
899 if(info == null) {
900 // Nothing to do log error message and return
901 Log.i(TAG, "Null ApplicationInfo for package:"+pkgName);
902 return;
903 }
904 // Binary search returns a negative index (ie --index) of the position where
905 // this might be inserted.
906 int newIdx = Collections.binarySearch(mAppLocalList, info,
907 getAppComparator(mSortOrder));
908 if(newIdx >= 0) {
909 Log.i(TAG, "Strange. Package:"+pkgName+" is not new");
910 return;
911 }
912 // New entry
913 newIdx = -newIdx-1;
914 mAppLocalList.add(newIdx, info);
915 mAppPropMap.put(info.packageName, new AppInfo(pkgName, newIdx,
916 info.loadLabel(mPm), info.loadIcon(mPm), ps));
917 adjustIndex();
918 notifyDataSetChanged();
919 }
920
921 public void removeFromList(List<String> pkgNames) {
922 if(pkgNames == null) {
923 Log.w(TAG, "Removing null pkg list from List Adapter");
924 return;
925 }
926 int imax = mAppLocalList.size();
927 boolean found = false;
928 ApplicationInfo info;
929 int i, k;
930 String pkgName;
931 int kmax = pkgNames.size();
932 if(kmax <= 0) {
933 Log.w(TAG, "Removing empty pkg list from List Adapter");
934 return;
935 }
936 int idxArr[] = new int[kmax];
937 for (k = 0; k < kmax; k++) {
938 idxArr[k] = -1;
939 }
940 for (i = 0; i < imax; i++) {
941 info = mAppLocalList.get(i);
942 for (k = 0; k < kmax; k++) {
943 pkgName = pkgNames.get(k);
944 if (info.packageName.equalsIgnoreCase(pkgName)) {
945 idxArr[k] = i;
946 found = true;
947 break;
948 }
949 }
950 }
951 // Sort idxArr
952 Arrays.sort(idxArr);
953 // remove the packages based on decending indices
954 for (k = kmax-1; k >= 0; k--) {
955 // Check if package has been found in the list of existing apps first
956 if(idxArr[k] == -1) {
957 break;
958 }
959 info = mAppLocalList.get(idxArr[k]);
960 mAppLocalList.remove(idxArr[k]);
961 mAppPropMap.remove(info.packageName);
962 if (localLOGV) Log.i(TAG, "Removed pkg:"+info.packageName+ " list");
963 }
964 if (found) {
965 adjustIndex();
966 notifyDataSetChanged();
967 }
968 }
969
970 public void updateAppSize(String pkgName, PackageStats ps) {
971 if(pkgName == null) {
972 return;
973 }
974 AppInfo entry = mAppPropMap.get(pkgName);
975 if (entry == null) {
976 Log.w(TAG, "Entry for package:"+pkgName+"doesnt exist in map");
977 return;
978 }
979 // Copy the index into the newly updated entry
980 entry.setSize(ps);
981 notifyDataSetChanged();
982 }
983
984 public PackageStats getAppStats(String pkgName) {
985 if(pkgName == null) {
986 return null;
987 }
988 AppInfo entry = mAppPropMap.get(pkgName);
989 if (entry == null) {
990 return null;
991 }
992 return entry.appStats;
993 }
994 }
995
996 /*
997 * Utility method to clear messages to Handler
998 * We need'nt synchronize on the Handler since posting messages is guaranteed
999 * to be thread safe. Even if the other thread that retrieves package sizes
1000 * posts a message, we do a cursory check of validity on mAppInfoAdapter's applist
1001 */
1002 private void clearMessagesInHandler() {
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001003 mHandler.removeMessages(INIT_PKG_INFO);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001004 mHandler.removeMessages(COMPUTE_PKG_SIZE_DONE);
1005 mHandler.removeMessages(REMOVE_PKG);
1006 mHandler.removeMessages(REORDER_LIST);
1007 mHandler.removeMessages(ADD_PKG_START);
1008 mHandler.removeMessages(ADD_PKG_DONE);
1009 }
1010
1011 private void sendMessageToHandler(int msgId, int arg1) {
1012 Message msg = mHandler.obtainMessage(msgId);
1013 msg.arg1 = arg1;
1014 mHandler.sendMessage(msg);
1015 }
1016
1017 private void sendMessageToHandler(int msgId, Bundle data) {
1018 Message msg = mHandler.obtainMessage(msgId);
1019 msg.setData(data);
1020 mHandler.sendMessage(msg);
1021 }
1022
1023 private void sendMessageToHandler(int msgId) {
1024 mHandler.sendEmptyMessage(msgId);
1025 }
1026
1027 /*
1028 * Stats Observer class used to compute package sizes and retrieve size information
1029 * PkgSizeOberver is the call back thats used when invoking getPackageSizeInfo on
1030 * PackageManager. The values in call back onGetStatsCompleted are validated
1031 * and the specified message is passed to mHandler. The package name
1032 * and the AppInfo object corresponding to the package name are set on the message
1033 */
1034 class PkgSizeObserver extends IPackageStatsObserver.Stub {
1035 private ApplicationInfo mAppInfo;
1036 private int mMsgId;
1037 public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) {
1038 if(DEBUG_PKG_DELAY) {
1039 try {
1040 Thread.sleep(10*1000);
1041 } catch (InterruptedException e) {
1042 }
1043 }
1044 AppInfo appInfo = null;
1045 Bundle data = new Bundle();
1046 data.putString(ATTR_PKG_NAME, mAppInfo.packageName);
1047 if(pSucceeded && pStats != null) {
1048 if (localLOGV) Log.i(TAG, "onGetStatsCompleted::"+pStats.packageName+", ("+
1049 pStats.cacheSize+","+
1050 pStats.codeSize+", "+pStats.dataSize);
1051 data.putParcelable(ATTR_APP_PKG_STATS, pStats);
1052 } else {
1053 Log.w(TAG, "Invalid package stats from PackageManager");
1054 }
1055 //post message to Handler
1056 Message msg = mHandler.obtainMessage(mMsgId, data);
1057 msg.setData(data);
1058 mHandler.sendMessage(msg);
1059 }
1060
1061 public void invokeGetSizeInfo(ApplicationInfo pAppInfo, int msgId) {
1062 if(pAppInfo == null || pAppInfo.packageName == null) {
1063 return;
1064 }
1065 if(localLOGV) Log.i(TAG, "Invoking getPackageSizeInfo for package:"+
1066 pAppInfo.packageName);
1067 mMsgId = msgId;
1068 mAppInfo = pAppInfo;
1069 mPm.getPackageSizeInfo(pAppInfo.packageName, this);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001070 }
1071 }
1072
1073 /**
1074 * Receives notifications when applications are added/removed.
1075 */
1076 private class PackageIntentReceiver extends BroadcastReceiver {
1077 void registerReceiver() {
1078 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1079 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1080 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1081 filter.addDataScheme("package");
1082 ManageApplications.this.registerReceiver(this, filter);
1083 }
1084 @Override
1085 public void onReceive(Context context, Intent intent) {
1086 String actionStr = intent.getAction();
1087 Uri data = intent.getData();
1088 String pkgName = data.getEncodedSchemeSpecificPart();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001089 if (localLOGV) Log.i(TAG, "action:"+actionStr+", for package:"+pkgName);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001090 updatePackageList(actionStr, pkgName);
1091 }
1092 }
1093
1094 private void updatePackageList(String actionStr, String pkgName) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001095 // technically we dont have to invoke handler since onReceive is invoked on
1096 // the main thread but doing it here for better clarity
1097 if (Intent.ACTION_PACKAGE_ADDED.equalsIgnoreCase(actionStr)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001098 Bundle data = new Bundle();
1099 data.putString(ATTR_PKG_NAME, pkgName);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001100 sendMessageToHandler(ADD_PKG_START, data);
1101 } else if (Intent.ACTION_PACKAGE_REMOVED.equalsIgnoreCase(actionStr)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001102 Bundle data = new Bundle();
1103 data.putString(ATTR_PKG_NAME, pkgName);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001104 sendMessageToHandler(REMOVE_PKG, data);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001105 }
1106 }
1107
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001108
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001109 @Override
1110 protected void onCreate(Bundle savedInstanceState) {
1111 super.onCreate(savedInstanceState);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001112 Intent lIntent = getIntent();
1113 String action = lIntent.getAction();
1114 if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001115 mSortOrder = SORT_ORDER_SIZE;
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001116 mSizesFirst = true;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001117 }
1118 mPm = getPackageManager();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001119 // initialize some window features
1120 requestWindowFeature(Window.FEATURE_RIGHT_ICON);
1121 requestWindowFeature(Window.FEATURE_PROGRESS);
1122 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001123 setContentView(R.layout.compute_sizes);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001124 mDefaultAppIcon =Resources.getSystem().getDrawable(
1125 com.android.internal.R.drawable.sym_def_app_icon);
1126 mInvalidSizeStr = getText(R.string.invalid_size_value);
1127 mComputingSizeStr = getText(R.string.computing_size);
1128 // initialize the inflater
1129 mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001130 mReceiver = new PackageIntentReceiver();
1131 mEmptyView = (TextView) findViewById(R.id.empty_view);
1132 mObserver = new PkgSizeObserver();
The Android Open Source Project8a156092009-03-02 22:54:43 -08001133 // Create adapter and list view here
1134 List<ApplicationInfo> appList = getInstalledApps(mSortOrder);
1135 mAppInfoAdapter = new AppInfoAdapter(this, appList);
1136 ListView lv= (ListView) findViewById(android.R.id.list);
1137 //lv.setAdapter(mAppInfoAdapter);
1138 lv.setOnItemClickListener(this);
1139 lv.setSaveEnabled(true);
1140 lv.setItemsCanFocus(true);
1141 lv.setOnItemClickListener(this);
1142 mListView = lv;
1143 showLoadingMsg();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001144 }
1145
The Android Open Source Project8a156092009-03-02 22:54:43 -08001146 @Override
1147 public Dialog onCreateDialog(int id) {
1148 if (id == DLG_LOADING) {
1149 ProgressDialog dlg = new ProgressDialog(this);
1150 dlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
1151 dlg.setMessage(getText(R.string.loading));
1152 dlg.setIndeterminate(true);
1153 dlg.setOnCancelListener(this);
1154 return dlg;
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001155 }
The Android Open Source Project8a156092009-03-02 22:54:43 -08001156 return null;
1157 }
1158
1159
1160 private void showLoadingMsg() {
1161 showDialog(DLG_LOADING);
1162 if(localLOGV) Log.i(TAG, "Displaying Loading message");
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001163 }
1164
1165 private void dismissLoadingMsg() {
The Android Open Source Project8a156092009-03-02 22:54:43 -08001166 if(localLOGV) Log.i(TAG, "Dismissing Loading message");
1167 dismissDialog(DLG_LOADING);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001168 }
1169
1170 @Override
1171 public void onStart() {
1172 super.onStart();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001173 // Create a thread to load resources
1174 mResourceThread = new ResourceLoaderThread();
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001175 sendMessageToHandler(INIT_PKG_INFO);
The Android Open Source Project8a156092009-03-02 22:54:43 -08001176 // register receiver
1177 mReceiver.registerReceiver();
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001178 }
1179
1180 @Override
1181 public void onStop() {
1182 super.onStop();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001183 // clear all messages related to application list
1184 clearMessagesInHandler();
1185 // register receiver here
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001186 unregisterReceiver(mReceiver);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001187 mAppPropCache = mAppInfoAdapter.mAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001188 }
1189
The Android Open Source Project8a156092009-03-02 22:54:43 -08001190 // Avoid the restart and pause when orientation changes
1191 @Override
1192 public void onConfigurationChanged(Configuration newConfig) {
1193 super.onConfigurationChanged(newConfig);
1194 }
1195
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001196 /*
1197 * comparator class used to sort AppInfo objects based on size
1198 */
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001199 public static class AppInfoComparator implements Comparator<ApplicationInfo> {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001200 public AppInfoComparator(Map<String, AppInfo> pAppPropMap) {
1201 mAppPropMap= pAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001202 }
1203
1204 public final int compare(ApplicationInfo a, ApplicationInfo b) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001205 AppInfo ainfo = mAppPropMap.get(a.packageName);
1206 AppInfo binfo = mAppPropMap.get(b.packageName);
1207 long atotal = ainfo.getTotalSize();
1208 long btotal = binfo.getTotalSize();
1209 long ret = atotal - btotal;
1210 // negate result to sort in descending order
1211 if (ret < 0) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001212 return 1;
1213 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001214 if (ret == 0) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001215 return 0;
1216 }
1217 return -1;
1218 }
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001219 private Map<String, AppInfo> mAppPropMap;
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001220 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001221
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001222 // utility method used to start sub activity
The Android Open Source Project8a156092009-03-02 22:54:43 -08001223 private void startApplicationDetailsActivity() {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001224 // Create intent to start new activity
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001225 Intent intent = new Intent(Intent.ACTION_VIEW);
1226 intent.setClass(this, InstalledAppDetails.class);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001227 intent.putExtra(APP_PKG_NAME, mCurrentPkgName);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001228 // start new activity to display extended information
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001229 startActivityForResult(intent, INSTALLED_APP_DETAILS);
1230 }
1231
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001232 @Override
1233 public boolean onCreateOptionsMenu(Menu menu) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001234 menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha)
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001235 .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001236 menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
1237 .setIcon(android.R.drawable.ic_menu_sort_by_size);
The Android Open Source Projectb9f58512009-02-13 12:57:53 -08001238 menu.add(0, FILTER_OPTIONS, 3, R.string.filter)
1239 .setIcon(R.drawable.ic_menu_filter_settings);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001240 return true;
1241 }
1242
1243 @Override
1244 public boolean onPrepareOptionsMenu(Menu menu) {
The Android Open Source Project8a156092009-03-02 22:54:43 -08001245 if (mFirst) {
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001246 menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001247 menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001248 menu.findItem(FILTER_OPTIONS).setVisible(true);
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001249 return true;
The Android Open Source Project8a156092009-03-02 22:54:43 -08001250 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001251 return false;
1252 }
1253
1254 @Override
1255 public boolean onOptionsItemSelected(MenuItem item) {
1256 int menuId = item.getItemId();
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001257 if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
1258 sendMessageToHandler(REORDER_LIST, menuId);
1259 } else if (menuId == FILTER_OPTIONS) {
The Android Open Source Project8a156092009-03-02 22:54:43 -08001260 if (mAlertDlg == null) {
1261 mAlertDlg = new AlertDialog.Builder(this).
1262 setTitle(R.string.filter_dlg_title).
1263 setNeutralButton(R.string.cancel, this).
1264 setSingleChoiceItems(new CharSequence[] {getText(R.string.filter_apps_all),
1265 getText(R.string.filter_apps_running),
1266 getText(R.string.filter_apps_third_party)},
1267 -1, this).
1268 create();
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001269 }
The Android Open Source Project8a156092009-03-02 22:54:43 -08001270 mAlertDlg.show();
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001271 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001272 return true;
1273 }
1274
1275 public void onItemClick(AdapterView<?> parent, View view, int position,
1276 long id) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001277 ApplicationInfo info = (ApplicationInfo)mAppInfoAdapter.getItem(position);
The Android Open Source Project8a156092009-03-02 22:54:43 -08001278 mCurrentPkgName = info.packageName;
1279 startApplicationDetailsActivity();
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001280 }
1281
The Android Open Source Project8a156092009-03-02 22:54:43 -08001282 // Finish the activity if the user presses the back button to cancel the activity
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001283 public void onCancel(DialogInterface dialog) {
The Android Open Source Projectabc48f82008-12-17 18:06:01 -08001284 finish();
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001285 }
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001286
1287 public void onClick(DialogInterface dialog, int which) {
1288 int newOption;
1289 switch (which) {
1290 // Make sure that values of 0, 1, 2 match options all, running, third_party when
1291 // created via the AlertDialog.Builder
1292 case 0:
1293 newOption = FILTER_APPS_ALL;
1294 break;
1295 case 1:
1296 newOption = FILTER_APPS_RUNNING;
1297 break;
1298 case 2:
1299 newOption = FILTER_APPS_THIRD_PARTY;
1300 break;
1301 default:
1302 return;
1303 }
The Android Open Source Project8a156092009-03-02 22:54:43 -08001304 mAlertDlg.dismiss();
The Android Open Source Project1feaa852009-02-10 15:44:05 -08001305 sendMessageToHandler(REORDER_LIST, newOption);
1306 }
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001307}