The Android Open Source Project | de2d9f5 | 2008-10-21 07:00:00 -0700 | [diff] [blame^] | 1 | /* |
| 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 | |
| 17 | package com.android.settings; |
| 18 | |
| 19 | import com.android.settings.R; |
| 20 | import android.app.Activity; |
| 21 | import android.content.BroadcastReceiver; |
| 22 | import android.content.Context; |
| 23 | import android.content.Intent; |
| 24 | import android.content.IntentFilter; |
| 25 | import android.content.pm.ApplicationInfo; |
| 26 | import android.content.pm.IPackageStatsObserver; |
| 27 | import android.content.pm.PackageManager; |
| 28 | import android.content.pm.PackageStats; |
| 29 | import android.content.pm.PackageManager.NameNotFoundException; |
| 30 | import android.graphics.drawable.Drawable; |
| 31 | import android.net.Uri; |
| 32 | import android.os.Bundle; |
| 33 | import android.os.Handler; |
| 34 | import android.os.Message; |
| 35 | import android.util.Config; |
| 36 | import android.util.Log; |
| 37 | import android.view.Menu; |
| 38 | import android.view.MenuItem; |
| 39 | import android.view.View; |
| 40 | import android.widget.AdapterView; |
| 41 | import android.widget.ImageView; |
| 42 | import android.widget.ListView; |
| 43 | import android.widget.SimpleAdapter; |
| 44 | import android.widget.TextView; |
| 45 | import android.widget.AdapterView.OnItemClickListener; |
| 46 | import java.util.ArrayList; |
| 47 | import java.util.Collections; |
| 48 | import java.util.Comparator; |
| 49 | import java.util.HashMap; |
| 50 | import java.util.List; |
| 51 | import java.util.Map; |
| 52 | import java.util.TreeMap; |
| 53 | |
| 54 | /** |
| 55 | * Activity to pick an application that will be used to display installation information and |
| 56 | * options to upgrade/uninstall/delete user data for system applications. |
| 57 | * Initially a compute in progress message is displayed while the application retrieves |
| 58 | * the size information of installed packages which is done asynchronously through a |
| 59 | * handler. Once the computation is done package resource information is retrieved |
| 60 | * and then the information is displayed on the screen. All |
| 61 | * messages are passed through a Handler object. |
| 62 | * Known issue: There could be some ordering issues when installing/uninstalling |
| 63 | * applications when the application list is being scanned. |
| 64 | */ |
| 65 | public class ManageApplications extends Activity implements SimpleAdapter.ViewBinder, OnItemClickListener { |
| 66 | private static final String TAG = "ManageApplications"; |
| 67 | //Application prefix information |
| 68 | public static final String APP_PKG_PREFIX="com.android.settings."; |
| 69 | public static final String APP_PKG_NAME=APP_PKG_PREFIX+"ApplicationPkgName"; |
| 70 | public static final String APP_PKG_SIZE= APP_PKG_PREFIX+"size"; |
| 71 | public static final String APP_CHG=APP_PKG_PREFIX+"changed"; |
| 72 | |
| 73 | //constant value that can be used to check return code from sub activity. |
| 74 | private static final int INSTALLED_APP_DETAILS = 1; |
| 75 | //application attributes passed to sub activity that displays more app info |
| 76 | private static final String KEY_APP_NAME = "ApplicationName"; |
| 77 | private static final String KEY_APP_ICON = "ApplicationIcon"; |
| 78 | private static final String KEY_APP_DESC = "ApplicationDescription"; |
| 79 | private static final String KEY_APP_SIZE= "ApplicationSize"; |
| 80 | //sort order that can be changed through the menu |
| 81 | public static final int SORT_ORDER_ALPHA = 0; |
| 82 | public static final int SORT_ORDER_SIZE = 1; |
| 83 | //key and resource values used in constructing map for SimpleAdapter |
| 84 | private static final String sKeys[] = new String[] { KEY_APP_NAME, KEY_APP_ICON, |
| 85 | KEY_APP_DESC, KEY_APP_SIZE}; |
| 86 | private static final int sResourceIds[] = new int[] { R.id.app_name, R.id.app_icon, |
| 87 | R.id.app_description, R.id.app_size}; |
| 88 | //List of ApplicationInfo objects for various applications |
| 89 | private List<ApplicationInfo> mAppList; |
| 90 | //SimpleAdapter used for managing items in the list |
| 91 | private SimpleAdapter mAppAdapter; |
| 92 | //map used to store size information which is used for displaying size information |
| 93 | //in this activity as well as the subactivity. this is to avoid invoking package manager |
| 94 | //api to retrieve size information |
| 95 | private HashMap<String, PackageStats> mSizeMap; |
| 96 | private HashMap<String, Map<String, ?> > mAppAdapterMap; |
| 97 | //sort order |
| 98 | private int mSortOrder = SORT_ORDER_ALPHA; |
| 99 | //log information boolean |
| 100 | private boolean localLOGV = Config.LOGV || false; |
| 101 | private ApplicationInfo mCurrentPkg; |
| 102 | private int mCurrentPkgIdx = 0; |
| 103 | private static final int COMPUTE_PKG_SIZE_START = 1; |
| 104 | private static final int COMPUTE_PKG_SIZE_DONE = 2; |
| 105 | private static final int REMOVE_PKG=3; |
| 106 | private static final int REORDER_LIST=4; |
| 107 | private static final int ADD_PKG=5; |
| 108 | private static final String ATTR_APP_IDX="ApplicationIndex"; |
| 109 | private static final String ATTR_CHAINED="Chained"; |
| 110 | private static final String ATTR_PKG_NAME="PackageName"; |
| 111 | private PkgSizeObserver mObserver; |
| 112 | private PackageManager mPm; |
| 113 | private PackageIntentReceiver mReceiver; |
| 114 | private boolean mDoneIniting = false; |
| 115 | private String mKbStr; |
| 116 | private String mMbStr; |
| 117 | private String mBStr; |
| 118 | |
| 119 | /* |
| 120 | * Handler class to handle messages for various operations |
| 121 | */ |
| 122 | private Handler mHandler = new Handler() { |
| 123 | public void handleMessage(Message msg) { |
| 124 | PackageStats ps; |
| 125 | ApplicationInfo info; |
| 126 | Bundle data; |
| 127 | String pkgName; |
| 128 | int idx; |
| 129 | int size; |
| 130 | boolean chained = false; |
| 131 | data = msg.getData(); |
| 132 | switch (msg.what) { |
| 133 | case COMPUTE_PKG_SIZE_START: |
| 134 | mDoneIniting = false; |
| 135 | //initialize lists |
| 136 | mAppList = new ArrayList<ApplicationInfo>(); |
| 137 | mSizeMap = new HashMap<String, PackageStats>(); |
| 138 | mAppAdapterMap = new HashMap<String, Map<String, ?> >(); |
| 139 | //update application list from PackageManager |
| 140 | mAppList = mPm.getInstalledApplications(0); |
| 141 | if(mAppList.size() == 0) { |
| 142 | return; |
| 143 | } |
| 144 | mCurrentPkgIdx = 0; |
| 145 | mCurrentPkg = mAppList.get(0); |
| 146 | if(localLOGV) Log.i(TAG, "Initiating compute sizes for first time"); |
| 147 | //register receiver |
| 148 | mReceiver = new PackageIntentReceiver(); |
| 149 | mReceiver.registerReceiver(); |
| 150 | pkgName = mCurrentPkg.packageName; |
| 151 | mObserver = new PkgSizeObserver(0); |
| 152 | mObserver.invokeGetSizeInfo(pkgName, true); |
| 153 | break; |
| 154 | case COMPUTE_PKG_SIZE_DONE: |
| 155 | ps = mObserver.ps; |
| 156 | info = mObserver.appInfo; |
| 157 | chained = data.getBoolean(ATTR_CHAINED); |
| 158 | if(!mObserver.succeeded) { |
| 159 | if(chained) { |
| 160 | removePackageFromAppList(ps.packageName); |
| 161 | } else { |
| 162 | //do not go to adding phase |
| 163 | break; |
| 164 | } |
| 165 | } else { |
| 166 | //insert size value |
| 167 | mSizeMap.put(ps.packageName, ps); |
| 168 | Map<String, Object> entry = createMapEntry(mPm.getApplicationLabel(info), |
| 169 | mPm.getApplicationIcon(info), |
| 170 | info.loadDescription(mPm), |
| 171 | getSizeStr(ps)); |
| 172 | mAppAdapterMap.put(ps.packageName, entry); |
| 173 | } |
| 174 | if(chained) { |
| 175 | //here app list is precomputed |
| 176 | idx = data.getInt(ATTR_APP_IDX); |
| 177 | //increment only if succeded |
| 178 | if(mObserver.succeeded) { |
| 179 | idx++; |
| 180 | } |
| 181 | if(idx < mAppList.size()) { |
| 182 | pkgName = mAppList.get(idx).packageName; |
| 183 | //increment record index and invoke getSizeInfo for next record |
| 184 | mObserver.invokeGetSizeInfo(pkgName, true); |
| 185 | } else { |
| 186 | sortAppList(); |
| 187 | createListFromValues(); |
| 188 | mDoneIniting = true; |
| 189 | } |
| 190 | } else { |
| 191 | //add app info object as well |
| 192 | mAppList.add(info); |
| 193 | sortAppList(); |
| 194 | size = mAppList.size(); |
| 195 | int i; |
| 196 | for(i = 0; i < size; i++) { |
| 197 | if(mAppList.get(i).packageName.equalsIgnoreCase(mCurrentPkg.packageName)) { |
| 198 | if(i > mCurrentPkgIdx) { |
| 199 | mCurrentPkgIdx = i; |
| 200 | } |
| 201 | break; |
| 202 | } |
| 203 | } |
| 204 | createListFromValues(); |
| 205 | } |
| 206 | break; |
| 207 | case REMOVE_PKG: |
| 208 | if(!mDoneIniting) { |
| 209 | //insert message again after some delay |
| 210 | sendMessageToHandler(REMOVE_PKG, data, 10*1000); |
| 211 | break; |
| 212 | } |
| 213 | pkgName = data.getString(ATTR_PKG_NAME); |
| 214 | removePackageFromAppList(pkgName); |
| 215 | if(mSizeMap.remove(pkgName) == null) { |
| 216 | Log.i(TAG, "Coudnt remove from size map package:"+pkgName); |
| 217 | } |
| 218 | if(mAppAdapterMap.remove(pkgName) == null) { |
| 219 | Log.i(TAG, "Coudnt remove from app adapter map package:"+pkgName); |
| 220 | } |
| 221 | if(mCurrentPkg.packageName.equalsIgnoreCase(pkgName)) { |
| 222 | if(mCurrentPkgIdx == (mAppList.size()-1)) { |
| 223 | mCurrentPkgIdx--; |
| 224 | } |
| 225 | mCurrentPkg = mAppList.get(mCurrentPkgIdx); |
| 226 | } |
| 227 | createListFromValues(); |
| 228 | break; |
| 229 | case REORDER_LIST: |
| 230 | int sortOrder = msg.arg1; |
| 231 | if(sortOrder != mSortOrder) { |
| 232 | mSortOrder = sortOrder; |
| 233 | if(localLOGV) Log.i(TAG, "Changing sort order to "+mSortOrder); |
| 234 | sortAppList(); |
| 235 | mCurrentPkgIdx = 0; |
| 236 | mCurrentPkg = mAppList.get(mCurrentPkgIdx); |
| 237 | createListFromValues(); |
| 238 | } |
| 239 | break; |
| 240 | case ADD_PKG: |
| 241 | pkgName = data.getString(ATTR_PKG_NAME); |
| 242 | if(!mDoneIniting) { |
| 243 | //insert message again after some delay |
| 244 | sendMessageToHandler(ADD_PKG, data, 10*1000); |
| 245 | break; |
| 246 | } |
| 247 | mObserver.invokeGetSizeInfo(pkgName, false); |
| 248 | break; |
| 249 | default: |
| 250 | break; |
| 251 | } |
| 252 | } |
| 253 | }; |
| 254 | |
| 255 | private void removePackageFromAppList(String pkgName) { |
| 256 | int size = mAppList.size(); |
| 257 | for(int i = 0; i < size; i++) { |
| 258 | if(mAppList.get(i).packageName.equalsIgnoreCase(pkgName)) { |
| 259 | mAppList.remove(i); |
| 260 | break; |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | private void clearMessages() { |
| 266 | synchronized(mHandler) { |
| 267 | mHandler.removeMessages(COMPUTE_PKG_SIZE_START); |
| 268 | mHandler.removeMessages(COMPUTE_PKG_SIZE_DONE); |
| 269 | mHandler.removeMessages(REMOVE_PKG); |
| 270 | mHandler.removeMessages(REORDER_LIST); |
| 271 | mHandler.removeMessages(ADD_PKG); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | private void sendMessageToHandler(int msgId, Bundle data, long delayMillis) { |
| 276 | synchronized(mHandler) { |
| 277 | Message msg = mHandler.obtainMessage(msgId); |
| 278 | msg.setData(data); |
| 279 | if(delayMillis == 0) { |
| 280 | mHandler.sendMessage(msg); |
| 281 | } else { |
| 282 | mHandler.sendMessageDelayed(msg, delayMillis); |
| 283 | } |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | private void sendMessageToHandler(int msgId, int arg1) { |
| 288 | synchronized(mHandler) { |
| 289 | Message msg = mHandler.obtainMessage(msgId); |
| 290 | msg.arg1 = arg1; |
| 291 | mHandler.sendMessage(msg); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | private void sendMessageToHandler(int msgId) { |
| 296 | synchronized(mHandler) { |
| 297 | mHandler.sendEmptyMessage(msgId); |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | class PkgSizeObserver extends IPackageStatsObserver.Stub { |
| 302 | public PackageStats ps; |
| 303 | public ApplicationInfo appInfo; |
| 304 | public Drawable appIcon; |
| 305 | public CharSequence appName; |
| 306 | public CharSequence appDesc = ""; |
| 307 | private int mIdx = 0; |
| 308 | private boolean mChained = false; |
| 309 | public boolean succeeded; |
| 310 | PkgSizeObserver(int i) { |
| 311 | mIdx = i; |
| 312 | } |
| 313 | |
| 314 | private void getAppDetails() { |
| 315 | try { |
| 316 | appInfo = mPm.getApplicationInfo(ps.packageName, 0); |
| 317 | } catch (NameNotFoundException e) { |
| 318 | return; |
| 319 | } |
| 320 | appName = appInfo.loadLabel(mPm); |
| 321 | appIcon = appInfo.loadIcon(mPm); |
| 322 | } |
| 323 | |
| 324 | public void onGetStatsCompleted(PackageStats pStats, boolean pSucceeded) { |
| 325 | Bundle data = new Bundle(); |
| 326 | ps = pStats; |
| 327 | succeeded = pSucceeded; |
| 328 | if(mChained) { |
| 329 | data.putInt(ATTR_APP_IDX, mIdx); |
| 330 | if(succeeded) { |
| 331 | mIdx++; |
| 332 | } |
| 333 | } |
| 334 | data.putBoolean(ATTR_CHAINED, mChained); |
| 335 | getAppDetails(); |
| 336 | if(localLOGV) Log.i(TAG, "onGetStatsCompleted::"+appInfo.packageName+", ("+ps.cacheSize+","+ |
| 337 | ps.codeSize+", "+ps.dataSize); |
| 338 | sendMessageToHandler(COMPUTE_PKG_SIZE_DONE, data, 0); |
| 339 | } |
| 340 | |
| 341 | public void invokeGetSizeInfo(String packageName, boolean chained) { |
| 342 | mChained = chained; |
| 343 | mPm.getPackageSizeInfo(packageName, this); |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | /** |
| 348 | * Receives notifications when applications are added/removed. |
| 349 | */ |
| 350 | private class PackageIntentReceiver extends BroadcastReceiver { |
| 351 | void registerReceiver() { |
| 352 | IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); |
| 353 | filter.addAction(Intent.ACTION_PACKAGE_REMOVED); |
| 354 | filter.addAction(Intent.ACTION_PACKAGE_CHANGED); |
| 355 | filter.addDataScheme("package"); |
| 356 | ManageApplications.this.registerReceiver(this, filter); |
| 357 | } |
| 358 | @Override |
| 359 | public void onReceive(Context context, Intent intent) { |
| 360 | String actionStr = intent.getAction(); |
| 361 | Uri data = intent.getData(); |
| 362 | String pkgName = data.getEncodedSchemeSpecificPart(); |
| 363 | if(localLOGV) Log.i(TAG, "action:"+actionStr+", for package:"+pkgName); |
| 364 | updatePackageList(actionStr, pkgName); |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | private void updatePackageList(String actionStr, String pkgName) { |
| 369 | //technically we dont have to invoke handler since onReceive is invoked on |
| 370 | //the main thread but doing it here for better clarity |
| 371 | if(Intent.ACTION_PACKAGE_ADDED.equalsIgnoreCase(actionStr)) { |
| 372 | Bundle data = new Bundle(); |
| 373 | data.putString(ATTR_PKG_NAME, pkgName); |
| 374 | sendMessageToHandler(ADD_PKG, data, 0); |
| 375 | } else if(Intent.ACTION_PACKAGE_REMOVED.equalsIgnoreCase(actionStr)) { |
| 376 | Bundle data = new Bundle(); |
| 377 | data.putString(ATTR_PKG_NAME, pkgName); |
| 378 | sendMessageToHandler(REMOVE_PKG, data, 0); |
| 379 | } else if(Intent.ACTION_PACKAGE_CHANGED.equalsIgnoreCase(actionStr)) { |
| 380 | //force adapter to draw the list again. TODO derive from SimpleAdapter |
| 381 | //to avoid this |
| 382 | |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | /* |
| 387 | * Utility method to create an array of map objects from a map of map objects |
| 388 | * for displaying list items to be used in SimpleAdapter. |
| 389 | */ |
| 390 | private void createListFromValues() { |
| 391 | findViewById(R.id.center_text).setVisibility(View.GONE); |
| 392 | populateAdapterList(); |
| 393 | mAppAdapter.setViewBinder(this); |
| 394 | ListView lv= (ListView) findViewById(android.R.id.list); |
| 395 | lv.setOnItemClickListener(this); |
| 396 | lv.setAdapter(mAppAdapter); |
| 397 | if(mCurrentPkgIdx != -1) { |
| 398 | lv.setSelection(mCurrentPkgIdx); |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | @Override |
| 403 | protected void onCreate(Bundle savedInstanceState) { |
| 404 | super.onCreate(savedInstanceState); |
| 405 | String action = getIntent().getAction(); |
| 406 | if(action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) { |
| 407 | mSortOrder = SORT_ORDER_SIZE; |
| 408 | } |
| 409 | mPm = getPackageManager(); |
| 410 | //load strings from resources |
| 411 | mBStr = getString(R.string.b_text); |
| 412 | mKbStr = getString(R.string.kb_text); |
| 413 | mMbStr = getString(R.string.mb_text); |
| 414 | } |
| 415 | |
| 416 | @Override |
| 417 | public void onStart() { |
| 418 | super.onStart(); |
| 419 | setContentView(R.layout.compute_sizes); |
| 420 | //clear all messages related to application list |
| 421 | clearMessages(); |
| 422 | sendMessageToHandler(COMPUTE_PKG_SIZE_START); |
| 423 | } |
| 424 | |
| 425 | @Override |
| 426 | public void onStop() { |
| 427 | super.onStop(); |
| 428 | //register receiver here |
| 429 | unregisterReceiver(mReceiver); |
| 430 | } |
| 431 | |
| 432 | public static class AppInfoComparator implements Comparator<ApplicationInfo> { |
| 433 | public AppInfoComparator(HashMap<String, PackageStats> pSizeMap) { |
| 434 | mSizeMap= pSizeMap; |
| 435 | } |
| 436 | |
| 437 | public final int compare(ApplicationInfo a, ApplicationInfo b) { |
| 438 | PackageStats aps, bps; |
| 439 | aps = mSizeMap.get(a.packageName); |
| 440 | bps = mSizeMap.get(b.packageName); |
| 441 | if (aps == null && bps == null) { |
| 442 | return 0; |
| 443 | } else if (aps == null) { |
| 444 | return 1; |
| 445 | } else if (bps == null) { |
| 446 | return -1; |
| 447 | } |
| 448 | long atotal = aps.dataSize+aps.codeSize+aps.cacheSize; |
| 449 | long btotal = bps.dataSize+bps.codeSize+bps.cacheSize; |
| 450 | long ret = atotal-btotal; |
| 451 | //negate result to sort in descending order |
| 452 | if(ret < 0) { |
| 453 | return 1; |
| 454 | } |
| 455 | if(ret == 0) { |
| 456 | return 0; |
| 457 | } |
| 458 | return -1; |
| 459 | } |
| 460 | private HashMap<String, PackageStats> mSizeMap; |
| 461 | } |
| 462 | |
| 463 | /* |
| 464 | * Have to extract elements form map and populate a list ot be used by |
| 465 | * SimpleAdapter when displaying list elements. The sort order has to follow |
| 466 | * the order of elements in mAppList. |
| 467 | */ |
| 468 | private List<Map<String, ?>> createAdapterListFromMap() { |
| 469 | //get the index from mAppInfo which gives the correct sort position |
| 470 | int imax = mAppList.size(); |
| 471 | if(localLOGV) Log.i(TAG, "Creating new adapter list"); |
| 472 | List<Map<String, ?>> adapterList = new ArrayList<Map<String, ?>>(); |
| 473 | ApplicationInfo tmpInfo; |
| 474 | for(int i = 0; i < imax; i++) { |
| 475 | tmpInfo = mAppList.get(i); |
| 476 | Map<String, Object>newObj = new TreeMap<String, Object>( |
| 477 | mAppAdapterMap.get(tmpInfo.packageName)); |
| 478 | adapterList.add(newObj); |
| 479 | } |
| 480 | return adapterList; |
| 481 | } |
| 482 | |
| 483 | private void populateAdapterList() { |
| 484 | mAppAdapter = new SimpleAdapter(this, createAdapterListFromMap(), |
| 485 | R.layout.manage_applications_item, sKeys, sResourceIds); |
| 486 | } |
| 487 | |
| 488 | private String getSizeStr(PackageStats ps) { |
| 489 | String retStr = ""; |
| 490 | //insert total size information into map to display in view |
| 491 | //at this point its guaranteed that ps is not null. but checking anyway |
| 492 | if(ps != null) { |
| 493 | long size = ps.cacheSize+ps.codeSize+ps.dataSize; |
| 494 | if(size < 1024) { |
| 495 | return String.valueOf(size)+mBStr; |
| 496 | } |
| 497 | long kb, mb, rem; |
| 498 | kb = size >> 10; |
| 499 | rem = size - (kb << 10); |
| 500 | if(kb < 1024) { |
| 501 | if(rem > 512) { |
| 502 | kb++; |
| 503 | } |
| 504 | retStr += String.valueOf(kb)+mKbStr; |
| 505 | return retStr; |
| 506 | } |
| 507 | mb = kb >> 10; |
| 508 | if(kb >= 512) { |
| 509 | //round off |
| 510 | mb++; |
| 511 | } |
| 512 | retStr += String.valueOf(mb)+ mMbStr; |
| 513 | return retStr; |
| 514 | } else { |
| 515 | Log.w(TAG, "Something fishy, cannot find size info for package:"+ps.packageName); |
| 516 | } |
| 517 | return retStr; |
| 518 | } |
| 519 | |
| 520 | public void sortAppList() { |
| 521 | // Sort application list |
| 522 | if(mSortOrder == SORT_ORDER_ALPHA) { |
| 523 | Collections.sort(mAppList, new ApplicationInfo.DisplayNameComparator(mPm)); |
| 524 | } else if(mSortOrder == SORT_ORDER_SIZE) { |
| 525 | Collections.sort(mAppList, new AppInfoComparator(mSizeMap)); |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | private Map<String, Object> createMapEntry(CharSequence appName, |
| 530 | Drawable appIcon, CharSequence appDesc, String sizeStr) { |
| 531 | Map<String, Object> map = new TreeMap<String, Object>(); |
| 532 | map.put(KEY_APP_NAME, appName); |
| 533 | //the icon cannot be null. if the application hasnt set it, the default icon is returned. |
| 534 | map.put(KEY_APP_ICON, appIcon); |
| 535 | if(appDesc == null) { |
| 536 | appDesc=""; |
| 537 | } |
| 538 | map.put(KEY_APP_DESC, appDesc); |
| 539 | map.put(KEY_APP_SIZE, sizeStr); |
| 540 | return map; |
| 541 | } |
| 542 | |
| 543 | private void startApplicationDetailsActivity(ApplicationInfo info, PackageStats ps) { |
| 544 | //Create intent to start new activity |
| 545 | Intent intent = new Intent(Intent.ACTION_VIEW); |
| 546 | intent.setClass(this, InstalledAppDetails.class); |
| 547 | intent.putExtra(APP_PKG_NAME, info.packageName); |
| 548 | if(localLOGV) Log.i(TAG, "code="+ps.codeSize+", cache="+ps.cacheSize+", data="+ps.dataSize); |
| 549 | intent.putExtra(APP_PKG_SIZE, ps); |
| 550 | if(localLOGV) Log.i(TAG, "Starting sub activity to display info for app:"+info |
| 551 | +" with intent:"+intent); |
| 552 | //start new activity to display extended information |
| 553 | if ((info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { |
| 554 | } |
| 555 | startActivityForResult(intent, INSTALLED_APP_DETAILS); |
| 556 | } |
| 557 | |
| 558 | public boolean setViewValue(View view, Object data, String textRepresentation) { |
| 559 | if(data == null) { |
| 560 | return false; |
| 561 | } |
| 562 | int id = view.getId(); |
| 563 | switch(id) { |
| 564 | case R.id.app_name: |
| 565 | ((TextView)view).setText((String)data); |
| 566 | break; |
| 567 | case R.id.app_icon: |
| 568 | ((ImageView)view).setImageDrawable((Drawable)data); |
| 569 | break; |
| 570 | case R.id.app_description: |
| 571 | ((TextView)view).setText((String)data); |
| 572 | break; |
| 573 | case R.id.app_size: |
| 574 | ((TextView)view).setText((String)data); |
| 575 | break; |
| 576 | default: |
| 577 | break; |
| 578 | } |
| 579 | return true; |
| 580 | } |
| 581 | |
| 582 | @Override |
| 583 | public boolean onCreateOptionsMenu(Menu menu) { |
| 584 | menu.add(0, SORT_ORDER_ALPHA, 0, R.string.sort_order_alpha) |
| 585 | .setIcon(android.R.drawable.ic_menu_sort_alphabetically); |
| 586 | menu.add(0, SORT_ORDER_SIZE, 0, R.string.sort_order_size) |
| 587 | .setIcon(android.R.drawable.ic_menu_sort_by_size); |
| 588 | return true; |
| 589 | } |
| 590 | |
| 591 | @Override |
| 592 | public boolean onPrepareOptionsMenu(Menu menu) { |
| 593 | if(mDoneIniting) { |
| 594 | menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA); |
| 595 | menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder!= SORT_ORDER_SIZE); |
| 596 | return true; |
| 597 | } |
| 598 | return false; |
| 599 | } |
| 600 | |
| 601 | @Override |
| 602 | public boolean onOptionsItemSelected(MenuItem item) { |
| 603 | int menuId = item.getItemId(); |
| 604 | sendMessageToHandler(REORDER_LIST, menuId); |
| 605 | return true; |
| 606 | } |
| 607 | |
| 608 | public void onItemClick(AdapterView<?> parent, View view, int position, |
| 609 | long id) { |
| 610 | mCurrentPkgIdx=position; |
| 611 | ApplicationInfo info = mAppList.get(position); |
| 612 | mCurrentPkg = info; |
| 613 | PackageStats ps = mSizeMap.get(info.packageName); |
| 614 | startApplicationDetailsActivity(info, ps); |
| 615 | } |
| 616 | } |