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