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