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