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.applications; 18 19 import com.android.internal.content.PackageHelper; 20 import com.android.settings.R; 21 import com.android.settings.applications.ApplicationsState.AppEntry; 22 23 import android.app.TabActivity; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.Intent; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.IPackageManager; 29 import android.content.pm.PackageInfo; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.os.Environment; 33 import android.os.RemoteException; 34 import android.os.ServiceManager; 35 import android.os.StatFs; 36 import android.provider.Settings; 37 import android.text.format.Formatter; 38 import android.util.Log; 39 import android.view.KeyEvent; 40 import android.view.LayoutInflater; 41 import android.view.Menu; 42 import android.view.MenuItem; 43 import android.view.View; 44 import android.view.ViewGroup; 45 import android.view.Window; 46 import android.view.animation.AnimationUtils; 47 import android.view.inputmethod.InputMethodManager; 48 import android.widget.AbsListView; 49 import android.widget.AdapterView; 50 import android.widget.BaseAdapter; 51 import android.widget.CheckBox; 52 import android.widget.Filter; 53 import android.widget.Filterable; 54 import android.widget.ImageView; 55 import android.widget.ListView; 56 import android.widget.TabHost; 57 import android.widget.TextView; 58 import android.widget.AdapterView.OnItemClickListener; 59 60 import java.util.ArrayList; 61 import java.util.Comparator; 62 63 final class CanBeOnSdCardChecker { 64 final IPackageManager mPm; 65 int mInstallLocation; 66 67 CanBeOnSdCardChecker() { 68 mPm = IPackageManager.Stub.asInterface( 69 ServiceManager.getService("package")); 70 } 71 72 void init() { 73 try { 74 mInstallLocation = mPm.getInstallLocation(); 75 } catch (RemoteException e) { 76 Log.e("CanBeOnSdCardChecker", "Is Package Manager running?"); 77 return; 78 } 79 } 80 81 boolean check(ApplicationInfo info) { 82 boolean canBe = false; 83 if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 84 canBe = true; 85 } else { 86 if ((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0 && 87 (info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 88 if (info.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL || 89 info.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { 90 canBe = true; 91 } else if (info.installLocation 92 == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) { 93 if (mInstallLocation == PackageHelper.APP_INSTALL_EXTERNAL) { 94 // For apps with no preference and the default value set 95 // to install on sdcard. 96 canBe = true; 97 } 98 } 99 } 100 } 101 return canBe; 102 } 103 } 104 105 /** 106 * Activity to pick an application that will be used to display installation information and 107 * options to uninstall/delete user data for system applications. This activity 108 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE 109 * intent. 110 */ 111 public class ManageApplications extends TabActivity implements 112 OnItemClickListener, DialogInterface.OnCancelListener, 113 TabHost.TabContentFactory, TabHost.OnTabChangeListener { 114 static final String TAG = "ManageApplications"; 115 static final boolean DEBUG = false; 116 117 // attributes used as keys when passing values to InstalledAppDetails activity 118 public static final String APP_CHG = "chg"; 119 120 // constant value that can be used to check return code from sub activity. 121 private static final int INSTALLED_APP_DETAILS = 1; 122 123 // sort order that can be changed through the menu can be sorted alphabetically 124 // or size(descending) 125 private static final int MENU_OPTIONS_BASE = 0; 126 // Filter options used for displayed list of applications 127 public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0; 128 public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1; 129 public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2; 130 131 public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4; 132 public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5; 133 public static final int SHOW_RUNNING_SERVICES = MENU_OPTIONS_BASE + 6; 134 public static final int SHOW_BACKGROUND_PROCESSES = MENU_OPTIONS_BASE + 7; 135 // sort order 136 private int mSortOrder = SORT_ORDER_ALPHA; 137 // Filter value 138 private int mFilterApps = FILTER_APPS_THIRD_PARTY; 139 140 private ApplicationsState mApplicationsState; 141 private ApplicationsAdapter mApplicationsAdapter; 142 143 // Size resource used for packages whose size computation failed for some reason 144 private CharSequence mInvalidSizeStr; 145 private CharSequence mComputingSizeStr; 146 147 // layout inflater object used to inflate views 148 private LayoutInflater mInflater; 149 150 private String mCurrentPkgName; 151 152 private View mLoadingContainer; 153 154 private View mListContainer; 155 156 // ListView used to display list 157 private ListView mListView; 158 // Custom view used to display running processes 159 private RunningProcessesView mRunningProcessesView; 160 161 LinearColorBar mColorBar; 162 TextView mStorageChartLabel; 163 TextView mUsedStorageText; 164 TextView mFreeStorageText; 165 166 // These are for keeping track of activity and tab switch state. 167 private int mCurView; 168 private boolean mCreatedRunning; 169 170 private boolean mResumedRunning; 171 private boolean mActivityResumed; 172 private Object mNonConfigInstance; 173 174 private StatFs mDataFileStats; 175 private StatFs mSDCardFileStats; 176 private boolean mLastShowedInternalStorage = true; 177 private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage; 178 179 final Runnable mRunningProcessesAvail = new Runnable() { 180 public void run() { 181 handleRunningProcessesAvail(); 182 } 183 }; 184 185 // View Holder used when displaying views 186 static class AppViewHolder { 187 ApplicationsState.AppEntry entry; 188 TextView appName; 189 ImageView appIcon; 190 TextView appSize; 191 TextView disabled; 192 CheckBox checkBox; 193 194 void updateSizeText(ManageApplications ma) { 195 if (DEBUG) Log.i(TAG, "updateSizeText of " + entry.label + " " + entry 196 + ": " + entry.sizeStr); 197 if (entry.sizeStr != null) { 198 appSize.setText(entry.sizeStr); 199 } else if (entry.size == ApplicationsState.SIZE_INVALID) { 200 appSize.setText(ma.mInvalidSizeStr); 201 } 202 } 203 } 204 205 /* 206 * Custom adapter implementation for the ListView 207 * This adapter maintains a map for each displayed application and its properties 208 * An index value on each AppInfo object indicates the correct position or index 209 * in the list. If the list gets updated dynamically when the user is viewing the list of 210 * applications, we need to return the correct index of position. This is done by mapping 211 * the getId methods via the package name into the internal maps and indices. 212 * The order of applications in the list is mirrored in mAppLocalList 213 */ 214 class ApplicationsAdapter extends BaseAdapter implements Filterable, 215 ApplicationsState.Callbacks, AbsListView.RecyclerListener { 216 private final ApplicationsState mState; 217 private final ArrayList<View> mActive = new ArrayList<View>(); 218 private ArrayList<ApplicationsState.AppEntry> mBaseEntries; 219 private ArrayList<ApplicationsState.AppEntry> mEntries; 220 private boolean mResumed; 221 private int mLastFilterMode=-1, mLastSortMode=-1; 222 private boolean mWaitingForData; 223 CharSequence mCurFilterPrefix; 224 225 private Filter mFilter = new Filter() { 226 @Override 227 protected FilterResults performFiltering(CharSequence constraint) { 228 ArrayList<ApplicationsState.AppEntry> entries 229 = applyPrefixFilter(constraint, mBaseEntries); 230 FilterResults fr = new FilterResults(); 231 fr.values = entries; 232 fr.count = entries.size(); 233 return fr; 234 } 235 236 @Override 237 protected void publishResults(CharSequence constraint, FilterResults results) { 238 mCurFilterPrefix = constraint; 239 mEntries = (ArrayList<ApplicationsState.AppEntry>)results.values; 240 notifyDataSetChanged(); 241 updateStorageUsage(); 242 } 243 }; 244 245 public ApplicationsAdapter(ApplicationsState state) { 246 mState = state; 247 } 248 249 public void resume(int filter, int sort) { 250 if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed); 251 if (!mResumed) { 252 mResumed = true; 253 mState.resume(this); 254 mLastFilterMode = filter; 255 mLastSortMode = sort; 256 rebuild(true); 257 } else { 258 rebuild(filter, sort); 259 } 260 } 261 262 public void pause() { 263 if (mResumed) { 264 mResumed = false; 265 mState.pause(); 266 } 267 } 268 269 public void rebuild(int filter, int sort) { 270 if (filter == mLastFilterMode && sort == mLastSortMode) { 271 return; 272 } 273 mLastFilterMode = filter; 274 mLastSortMode = sort; 275 rebuild(true); 276 } 277 278 public void rebuild(boolean eraseold) { 279 if (DEBUG) Log.i(TAG, "Rebuilding app list..."); 280 ApplicationsState.AppFilter filterObj; 281 Comparator<AppEntry> comparatorObj; 282 switch (mLastFilterMode) { 283 case FILTER_APPS_THIRD_PARTY: 284 filterObj = ApplicationsState.THIRD_PARTY_FILTER; 285 break; 286 case FILTER_APPS_SDCARD: 287 filterObj = ApplicationsState.ON_SD_CARD_FILTER; 288 break; 289 default: 290 filterObj = null; 291 break; 292 } 293 switch (mLastSortMode) { 294 case SORT_ORDER_SIZE: 295 comparatorObj = ApplicationsState.SIZE_COMPARATOR; 296 break; 297 default: 298 comparatorObj = ApplicationsState.ALPHA_COMPARATOR; 299 break; 300 } 301 ArrayList<ApplicationsState.AppEntry> entries 302 = mState.rebuild(filterObj, comparatorObj); 303 if (entries == null && !eraseold) { 304 // Don't have new list yet, but can continue using the old one. 305 return; 306 } 307 mBaseEntries = entries; 308 if (mBaseEntries != null) { 309 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries); 310 } else { 311 mEntries = null; 312 } 313 notifyDataSetChanged(); 314 updateStorageUsage(); 315 316 if (entries == null) { 317 mWaitingForData = true; 318 mListContainer.setVisibility(View.INVISIBLE); 319 mLoadingContainer.setVisibility(View.VISIBLE); 320 } else { 321 mListContainer.setVisibility(View.VISIBLE); 322 mLoadingContainer.setVisibility(View.GONE); 323 } 324 } 325 326 ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix, 327 ArrayList<ApplicationsState.AppEntry> origEntries) { 328 if (prefix == null || prefix.length() == 0) { 329 return origEntries; 330 } else { 331 String prefixStr = ApplicationsState.normalize(prefix.toString()); 332 final String spacePrefixStr = " " + prefixStr; 333 ArrayList<ApplicationsState.AppEntry> newEntries 334 = new ArrayList<ApplicationsState.AppEntry>(); 335 for (int i=0; i<origEntries.size(); i++) { 336 ApplicationsState.AppEntry entry = origEntries.get(i); 337 String nlabel = entry.getNormalizedLabel(); 338 if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) { 339 newEntries.add(entry); 340 } 341 } 342 return newEntries; 343 } 344 } 345 346 @Override 347 public void onRunningStateChanged(boolean running) { 348 setProgressBarIndeterminateVisibility(running); 349 } 350 351 @Override 352 public void onRebuildComplete(ArrayList<AppEntry> apps) { 353 if (mLoadingContainer.getVisibility() == View.VISIBLE) { 354 mLoadingContainer.startAnimation(AnimationUtils.loadAnimation( 355 ManageApplications.this, android.R.anim.fade_out)); 356 mListContainer.startAnimation(AnimationUtils.loadAnimation( 357 ManageApplications.this, android.R.anim.fade_in)); 358 } 359 mListContainer.setVisibility(View.VISIBLE); 360 mLoadingContainer.setVisibility(View.GONE); 361 mWaitingForData = false; 362 mBaseEntries = apps; 363 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries); 364 notifyDataSetChanged(); 365 updateStorageUsage(); 366 } 367 368 @Override 369 public void onPackageListChanged() { 370 rebuild(false); 371 } 372 373 @Override 374 public void onPackageIconChanged() { 375 // We ensure icons are loaded when their item is displayed, so 376 // don't care about icons loaded in the background. 377 } 378 379 @Override 380 public void onPackageSizeChanged(String packageName) { 381 for (int i=0; i<mActive.size(); i++) { 382 AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag(); 383 if (holder.entry.info.packageName.equals(packageName)) { 384 synchronized (holder.entry) { 385 holder.updateSizeText(ManageApplications.this); 386 } 387 if (holder.entry.info.packageName.equals(mCurrentPkgName) 388 && mLastSortMode == SORT_ORDER_SIZE) { 389 // We got the size information for the last app the 390 // user viewed, and are sorting by size... they may 391 // have cleared data, so we immediately want to resort 392 // the list with the new size to reflect it to the user. 393 rebuild(false); 394 } 395 updateStorageUsage(); 396 return; 397 } 398 } 399 } 400 401 @Override 402 public void onAllSizesComputed() { 403 if (mLastSortMode == SORT_ORDER_SIZE) { 404 rebuild(false); 405 } 406 } 407 408 public int getCount() { 409 return mEntries != null ? mEntries.size() : 0; 410 } 411 412 public Object getItem(int position) { 413 return mEntries.get(position); 414 } 415 416 public ApplicationsState.AppEntry getAppEntry(int position) { 417 return mEntries.get(position); 418 } 419 420 public long getItemId(int position) { 421 return mEntries.get(position).id; 422 } 423 424 public View getView(int position, View convertView, ViewGroup parent) { 425 // A ViewHolder keeps references to children views to avoid unnecessary calls 426 // to findViewById() on each row. 427 AppViewHolder holder; 428 429 // When convertView is not null, we can reuse it directly, there is no need 430 // to reinflate it. We only inflate a new View when the convertView supplied 431 // by ListView is null. 432 if (convertView == null) { 433 convertView = mInflater.inflate(R.layout.manage_applications_item, null); 434 435 // Creates a ViewHolder and store references to the two children views 436 // we want to bind data to. 437 holder = new AppViewHolder(); 438 holder.appName = (TextView) convertView.findViewById(R.id.app_name); 439 holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon); 440 holder.appSize = (TextView) convertView.findViewById(R.id.app_size); 441 holder.disabled = (TextView) convertView.findViewById(R.id.app_disabled); 442 holder.checkBox = (CheckBox) convertView.findViewById(R.id.app_on_sdcard); 443 convertView.setTag(holder); 444 } else { 445 // Get the ViewHolder back to get fast access to the TextView 446 // and the ImageView. 447 holder = (AppViewHolder) convertView.getTag(); 448 } 449 450 // Bind the data efficiently with the holder 451 ApplicationsState.AppEntry entry = mEntries.get(position); 452 synchronized (entry) { 453 holder.entry = entry; 454 if (entry.label != null) { 455 holder.appName.setText(entry.label); 456 holder.appName.setTextColor(getResources().getColorStateList( 457 entry.info.enabled ? android.R.color.primary_text_dark 458 : android.R.color.secondary_text_dark)); 459 } 460 mState.ensureIcon(entry); 461 if (entry.icon != null) { 462 holder.appIcon.setImageDrawable(entry.icon); 463 } 464 holder.updateSizeText(ManageApplications.this); 465 if (InstalledAppDetails.SUPPORT_DISABLE_APPS) { 466 holder.disabled.setVisibility(entry.info.enabled ? View.GONE : View.VISIBLE); 467 } else { 468 holder.disabled.setVisibility(View.GONE); 469 } 470 if (mLastFilterMode == FILTER_APPS_SDCARD) { 471 holder.checkBox.setVisibility(View.VISIBLE); 472 holder.checkBox.setChecked((entry.info.flags 473 & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); 474 } else { 475 holder.checkBox.setVisibility(View.GONE); 476 } 477 } 478 mActive.remove(convertView); 479 mActive.add(convertView); 480 return convertView; 481 } 482 483 @Override 484 public Filter getFilter() { 485 return mFilter; 486 } 487 488 @Override 489 public void onMovedToScrapHeap(View view) { 490 mActive.remove(view); 491 } 492 } 493 494 static final String TAB_DOWNLOADED = "Downloaded"; 495 static final String TAB_RUNNING = "Running"; 496 static final String TAB_ALL = "All"; 497 static final String TAB_SDCARD = "OnSdCard"; 498 private View mRootView; 499 500 @Override 501 protected void onCreate(Bundle savedInstanceState) { 502 super.onCreate(savedInstanceState); 503 mApplicationsState = ApplicationsState.getInstance(getApplication()); 504 mApplicationsAdapter = new ApplicationsAdapter(mApplicationsState); 505 Intent intent = getIntent(); 506 String action = intent.getAction(); 507 String defaultTabTag = TAB_DOWNLOADED; 508 if (intent.getComponent().getClassName().equals( 509 "com.android.settings.RunningServices")) { 510 defaultTabTag = TAB_RUNNING; 511 } else if (intent.getComponent().getClassName().equals( 512 "com.android.settings.applications.StorageUse") 513 || action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) { 514 mSortOrder = SORT_ORDER_SIZE; 515 mFilterApps = FILTER_APPS_ALL; 516 defaultTabTag = TAB_ALL; 517 } else if (action.equals(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS)) { 518 // Select the all-apps tab, with the default sorting 519 defaultTabTag = TAB_ALL; 520 } 521 522 if (savedInstanceState != null) { 523 mSortOrder = savedInstanceState.getInt("sortOrder", mSortOrder); 524 mFilterApps = savedInstanceState.getInt("filterApps", mFilterApps); 525 String tmp = savedInstanceState.getString("defaultTabTag"); 526 if (tmp != null) defaultTabTag = tmp; 527 } 528 529 mNonConfigInstance = getLastNonConfigurationInstance(); 530 531 mDataFileStats = new StatFs("/data"); 532 mSDCardFileStats = new StatFs(Environment.getExternalStorageDirectory().toString()); 533 534 // initialize some window features 535 requestWindowFeature(Window.FEATURE_RIGHT_ICON); 536 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 537 mInvalidSizeStr = getText(R.string.invalid_size_value); 538 mComputingSizeStr = getText(R.string.computing_size); 539 // initialize the inflater 540 mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); 541 mRootView = mInflater.inflate(R.layout.manage_applications, null); 542 mLoadingContainer = mRootView.findViewById(R.id.loading_container); 543 mListContainer = mRootView.findViewById(R.id.list_container); 544 // Create adapter and list view here 545 ListView lv = (ListView) mListContainer.findViewById(android.R.id.list); 546 View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty); 547 if (emptyView != null) { 548 lv.setEmptyView(emptyView); 549 } 550 lv.setOnItemClickListener(this); 551 lv.setSaveEnabled(true); 552 lv.setItemsCanFocus(true); 553 lv.setOnItemClickListener(this); 554 lv.setTextFilterEnabled(true); 555 mListView = lv; 556 lv.setRecyclerListener(mApplicationsAdapter); 557 mListView.setAdapter(mApplicationsAdapter); 558 mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar); 559 mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel); 560 mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText); 561 mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText); 562 mRunningProcessesView = (RunningProcessesView)mRootView.findViewById( 563 R.id.running_processes); 564 565 final TabHost tabHost = getTabHost(); 566 tabHost.addTab(tabHost.newTabSpec(TAB_DOWNLOADED) 567 .setIndicator(getString(R.string.filter_apps_third_party), 568 getResources().getDrawable(R.drawable.ic_tab_download)) 569 .setContent(this)); 570 tabHost.addTab(tabHost.newTabSpec(TAB_ALL) 571 .setIndicator(getString(R.string.filter_apps_all), 572 getResources().getDrawable(R.drawable.ic_tab_all)) 573 .setContent(this)); 574 tabHost.addTab(tabHost.newTabSpec(TAB_SDCARD) 575 .setIndicator(getString(R.string.filter_apps_onsdcard), 576 getResources().getDrawable(R.drawable.ic_tab_sdcard)) 577 .setContent(this)); 578 tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING) 579 .setIndicator(getString(R.string.filter_apps_running), 580 getResources().getDrawable(R.drawable.ic_tab_running)) 581 .setContent(this)); 582 tabHost.setCurrentTabByTag(defaultTabTag); 583 tabHost.setOnTabChangedListener(this); 584 } 585 586 @Override 587 public void onStart() { 588 super.onStart(); 589 } 590 591 @Override 592 protected void onResume() { 593 super.onResume(); 594 mActivityResumed = true; 595 showCurrentTab(); 596 } 597 598 @Override 599 protected void onSaveInstanceState(Bundle outState) { 600 super.onSaveInstanceState(outState); 601 outState.putInt("sortOrder", mSortOrder); 602 outState.putInt("filterApps", mFilterApps); 603 outState.putString("defautTabTag", getTabHost().getCurrentTabTag()); 604 } 605 606 @Override 607 public Object onRetainNonConfigurationInstance() { 608 return mRunningProcessesView.doRetainNonConfigurationInstance(); 609 } 610 611 @Override 612 protected void onPause() { 613 super.onPause(); 614 mActivityResumed = false; 615 mApplicationsAdapter.pause(); 616 if (mResumedRunning) { 617 mRunningProcessesView.doPause(); 618 mResumedRunning = false; 619 } 620 } 621 622 @Override 623 protected void onActivityResult(int requestCode, int resultCode, 624 Intent data) { 625 if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { 626 mApplicationsState.requestSize(mCurrentPkgName); 627 } 628 } 629 630 // utility method used to start sub activity 631 private void startApplicationDetailsActivity() { 632 // Create intent to start new activity 633 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 634 Uri.fromParts("package", mCurrentPkgName, null)); 635 // start new activity to display extended information 636 startActivityForResult(intent, INSTALLED_APP_DETAILS); 637 } 638 639 @Override 640 public boolean onCreateOptionsMenu(Menu menu) { 641 menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha) 642 .setIcon(android.R.drawable.ic_menu_sort_alphabetically); 643 menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size) 644 .setIcon(android.R.drawable.ic_menu_sort_by_size); 645 menu.add(0, SHOW_RUNNING_SERVICES, 3, R.string.show_running_services); 646 menu.add(0, SHOW_BACKGROUND_PROCESSES, 3, R.string.show_background_processes); 647 return true; 648 } 649 650 @Override 651 public boolean onPrepareOptionsMenu(Menu menu) { 652 /* 653 * The running processes screen doesn't use the mApplicationsAdapter 654 * so bringing up this menu in that case doesn't make any sense. 655 */ 656 if (mCurView == VIEW_RUNNING) { 657 boolean showingBackground = mRunningProcessesView.mAdapter.getShowBackground(); 658 menu.findItem(SORT_ORDER_ALPHA).setVisible(false); 659 menu.findItem(SORT_ORDER_SIZE).setVisible(false); 660 menu.findItem(SHOW_RUNNING_SERVICES).setVisible(showingBackground); 661 menu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(!showingBackground); 662 } else { 663 menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA); 664 menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE); 665 menu.findItem(SHOW_RUNNING_SERVICES).setVisible(false); 666 menu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(false); 667 } 668 return true; 669 } 670 671 @Override 672 public boolean onOptionsItemSelected(MenuItem item) { 673 int menuId = item.getItemId(); 674 if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) { 675 mSortOrder = menuId; 676 if (mCurView != VIEW_RUNNING) { 677 mApplicationsAdapter.rebuild(mFilterApps, mSortOrder); 678 } 679 } else if (menuId == SHOW_RUNNING_SERVICES) { 680 mRunningProcessesView.mAdapter.setShowBackground(false); 681 } else if (menuId == SHOW_BACKGROUND_PROCESSES) { 682 mRunningProcessesView.mAdapter.setShowBackground(true); 683 } 684 return true; 685 } 686 687 @Override 688 public boolean onKeyUp(int keyCode, KeyEvent event) { 689 if (keyCode == KeyEvent.KEYCODE_SEARCH && event.isTracking()) { 690 if (mCurView != VIEW_RUNNING) { 691 ((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE)) 692 .showSoftInputUnchecked(0, null); 693 } 694 return true; 695 } 696 return super.onKeyUp(keyCode, event); 697 } 698 699 public void onItemClick(AdapterView<?> parent, View view, int position, 700 long id) { 701 ApplicationsState.AppEntry entry = mApplicationsAdapter.getAppEntry(position); 702 mCurrentPkgName = entry.info.packageName; 703 startApplicationDetailsActivity(); 704 } 705 706 // Finish the activity if the user presses the back button to cancel the activity 707 public void onCancel(DialogInterface dialog) { 708 finish(); 709 } 710 711 public View createTabContent(String tag) { 712 return mRootView; 713 } 714 715 static final int VIEW_NOTHING = 0; 716 static final int VIEW_LIST = 1; 717 static final int VIEW_RUNNING = 2; 718 719 void updateStorageUsage() { 720 if (mCurView == VIEW_RUNNING) { 721 return; 722 } 723 724 long freeStorage = 0; 725 long appStorage = 0; 726 long totalStorage = 0; 727 CharSequence newLabel = null; 728 729 if (mFilterApps == FILTER_APPS_SDCARD) { 730 if (mLastShowedInternalStorage) { 731 mLastShowedInternalStorage = false; 732 } 733 newLabel = this.getText(R.string.sd_card_storage); 734 mSDCardFileStats.restat(Environment.getExternalStorageDirectory().toString()); 735 try { 736 totalStorage = (long)mSDCardFileStats.getBlockCount() * 737 mSDCardFileStats.getBlockSize(); 738 freeStorage = (long) mSDCardFileStats.getAvailableBlocks() * 739 mSDCardFileStats.getBlockSize(); 740 } catch (IllegalArgumentException e) { 741 // use the old value of mFreeMem 742 } 743 } else { 744 if (!mLastShowedInternalStorage) { 745 mLastShowedInternalStorage = true; 746 } 747 newLabel = this.getText(R.string.internal_storage); 748 mDataFileStats.restat("/data"); 749 try { 750 totalStorage = (long)mDataFileStats.getBlockCount() * 751 mDataFileStats.getBlockSize(); 752 freeStorage = (long) mDataFileStats.getAvailableBlocks() * 753 mDataFileStats.getBlockSize(); 754 } catch (IllegalArgumentException e) { 755 } 756 final int N = mApplicationsAdapter.getCount(); 757 for (int i=0; i<N; i++) { 758 ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i); 759 appStorage += ae.codeSize + ae.dataSize; 760 } 761 freeStorage += mApplicationsState.sumCacheSizes(); 762 } 763 if (newLabel != null) { 764 mStorageChartLabel.setText(newLabel); 765 } 766 if (totalStorage > 0) { 767 mColorBar.setRatios((totalStorage-freeStorage-appStorage)/(float)totalStorage, 768 appStorage/(float)totalStorage, freeStorage/(float)totalStorage); 769 long usedStorage = totalStorage - freeStorage; 770 if (mLastUsedStorage != usedStorage) { 771 mLastUsedStorage = usedStorage; 772 String sizeStr = Formatter.formatShortFileSize(this, usedStorage); 773 mUsedStorageText.setText(getResources().getString( 774 R.string.service_foreground_processes, sizeStr)); 775 } 776 if (mLastFreeStorage != freeStorage) { 777 mLastFreeStorage = freeStorage; 778 String sizeStr = Formatter.formatShortFileSize(this, freeStorage); 779 mFreeStorageText.setText(getResources().getString( 780 R.string.service_background_processes, sizeStr)); 781 } 782 } else { 783 mColorBar.setRatios(0, 0, 0); 784 if (mLastUsedStorage != -1) { 785 mLastUsedStorage = -1; 786 mUsedStorageText.setText(""); 787 } 788 if (mLastFreeStorage != -1) { 789 mLastFreeStorage = -1; 790 mFreeStorageText.setText(""); 791 } 792 } 793 } 794 795 private void selectView(int which) { 796 if (which == VIEW_LIST) { 797 if (mResumedRunning) { 798 mRunningProcessesView.doPause(); 799 mResumedRunning = false; 800 } 801 if (mCurView != which) { 802 mRunningProcessesView.setVisibility(View.GONE); 803 mListContainer.setVisibility(View.VISIBLE); 804 mLoadingContainer.setVisibility(View.GONE); 805 } 806 if (mActivityResumed) { 807 mApplicationsAdapter.resume(mFilterApps, mSortOrder); 808 } 809 } else if (which == VIEW_RUNNING) { 810 if (!mCreatedRunning) { 811 mRunningProcessesView.doCreate(null, mNonConfigInstance); 812 mCreatedRunning = true; 813 } 814 boolean haveData = true; 815 if (mActivityResumed && !mResumedRunning) { 816 haveData = mRunningProcessesView.doResume(mRunningProcessesAvail); 817 mResumedRunning = true; 818 } 819 mApplicationsAdapter.pause(); 820 if (mCurView != which) { 821 if (haveData) { 822 mRunningProcessesView.setVisibility(View.VISIBLE); 823 } else { 824 mLoadingContainer.setVisibility(View.VISIBLE); 825 } 826 mListContainer.setVisibility(View.GONE); 827 } 828 } 829 mCurView = which; 830 } 831 832 void handleRunningProcessesAvail() { 833 if (mCurView == VIEW_RUNNING) { 834 mLoadingContainer.startAnimation(AnimationUtils.loadAnimation( 835 this, android.R.anim.fade_out)); 836 mRunningProcessesView.startAnimation(AnimationUtils.loadAnimation( 837 this, android.R.anim.fade_in)); 838 mRunningProcessesView.setVisibility(View.VISIBLE); 839 mLoadingContainer.setVisibility(View.GONE); 840 } 841 } 842 843 public void showCurrentTab() { 844 String tabId = getTabHost().getCurrentTabTag(); 845 int newOption; 846 if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) { 847 newOption = FILTER_APPS_THIRD_PARTY; 848 } else if (TAB_ALL.equalsIgnoreCase(tabId)) { 849 newOption = FILTER_APPS_ALL; 850 } else if (TAB_SDCARD.equalsIgnoreCase(tabId)) { 851 newOption = FILTER_APPS_SDCARD; 852 } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) { 853 ((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE)) 854 .hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0); 855 selectView(VIEW_RUNNING); 856 return; 857 } else { 858 // Invalid option. Do nothing 859 return; 860 } 861 862 mFilterApps = newOption; 863 selectView(VIEW_LIST); 864 updateStorageUsage(); 865 } 866 867 public void onTabChanged(String tabId) { 868 showCurrentTab(); 869 } 870 } 871