ManageApplications.java revision 07025891683aef83d850b0a91b37aa75b11ce33c
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 android.net.NetworkPolicyManager.POLICY_NONE; 20import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; 21 22import android.app.Activity; 23import android.app.ActivityManager; 24import android.app.AlertDialog; 25import android.app.AppOpsManager; 26import android.app.Fragment; 27import android.app.INotificationManager; 28import android.content.ComponentName; 29import android.content.Context; 30import android.content.DialogInterface; 31import android.content.Intent; 32import android.content.ServiceConnection; 33import android.content.pm.ApplicationInfo; 34import android.content.pm.IPackageManager; 35import android.content.pm.PackageInfo; 36import android.content.pm.PackageManager; 37import android.net.NetworkPolicyManager; 38import android.net.Uri; 39import android.os.AsyncTask; 40import android.os.Bundle; 41import android.os.Environment; 42import android.os.Handler; 43import android.os.IBinder; 44import android.os.RemoteException; 45import android.os.ServiceManager; 46import android.os.UserHandle; 47import android.os.UserManager; 48import android.preference.PreferenceFrameLayout; 49import android.provider.Settings; 50import android.support.v4.view.PagerAdapter; 51import android.support.v4.view.PagerTabStrip; 52import android.support.v4.view.ViewPager; 53import android.util.Log; 54import android.view.LayoutInflater; 55import android.view.Menu; 56import android.view.MenuInflater; 57import android.view.MenuItem; 58import android.view.View; 59import android.view.ViewGroup; 60import android.view.animation.AnimationUtils; 61import android.widget.AbsListView; 62import android.widget.AdapterView; 63import android.widget.AdapterView.OnItemClickListener; 64import android.widget.AdapterView.OnItemSelectedListener; 65import android.widget.BaseAdapter; 66import android.widget.Filter; 67import android.widget.Filterable; 68import android.widget.ListView; 69import android.widget.Spinner; 70 71import com.android.internal.app.IMediaContainerService; 72import com.android.internal.content.PackageHelper; 73import com.android.settings.R; 74import com.android.settings.Settings.RunningServicesActivity; 75import com.android.settings.Settings.StorageUseActivity; 76import com.android.settings.UserSpinnerAdapter; 77import com.android.settings.Utils; 78import com.android.settings.applications.ApplicationsState.AppEntry; 79import com.android.settings.deviceinfo.StorageMeasurement; 80 81import java.util.ArrayList; 82import java.util.Comparator; 83import java.util.List; 84 85final class CanBeOnSdCardChecker { 86 final IPackageManager mPm; 87 int mInstallLocation; 88 89 CanBeOnSdCardChecker() { 90 mPm = IPackageManager.Stub.asInterface( 91 ServiceManager.getService("package")); 92 } 93 94 void init() { 95 try { 96 mInstallLocation = mPm.getInstallLocation(); 97 } catch (RemoteException e) { 98 Log.e("CanBeOnSdCardChecker", "Is Package Manager running?"); 99 return; 100 } 101 } 102 103 boolean check(ApplicationInfo info) { 104 boolean canBe = false; 105 if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 106 canBe = true; 107 } else { 108 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 109 if (info.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL || 110 info.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { 111 canBe = true; 112 } else if (info.installLocation 113 == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) { 114 if (mInstallLocation == PackageHelper.APP_INSTALL_EXTERNAL) { 115 // For apps with no preference and the default value set 116 // to install on sdcard. 117 canBe = true; 118 } 119 } 120 } 121 } 122 return canBe; 123 } 124} 125 126interface AppClickListener { 127 void onItemClick(ManageApplications.TabInfo tab, AdapterView<?> parent, 128 View view, int position, long id); 129} 130 131/** 132 * Activity to pick an application that will be used to display installation information and 133 * options to uninstall/delete user data for system applications. This activity 134 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE 135 * intent. 136 */ 137public class ManageApplications extends Fragment implements 138 AppClickListener, DialogInterface.OnClickListener, DialogInterface.OnDismissListener { 139 140 static final String TAG = "ManageApplications"; 141 static final boolean DEBUG = false; 142 143 private static final String EXTRA_LIST_TYPE = "currentListType"; 144 private static final String EXTRA_SORT_ORDER = "sortOrder"; 145 private static final String EXTRA_SHOW_BACKGROUND = "showBackground"; 146 private static final String EXTRA_DEFAULT_LIST_TYPE = "defaultListType"; 147 private static final String EXTRA_RESET_DIALOG = "resetDialog"; 148 149 // attributes used as keys when passing values to InstalledAppDetails activity 150 public static final String APP_CHG = "chg"; 151 152 // constant value that can be used to check return code from sub activity. 153 private static final int INSTALLED_APP_DETAILS = 1; 154 155 public static final int SIZE_TOTAL = 0; 156 public static final int SIZE_INTERNAL = 1; 157 public static final int SIZE_EXTERNAL = 2; 158 159 // sort order that can be changed through the menu can be sorted alphabetically 160 // or size(descending) 161 private static final int MENU_OPTIONS_BASE = 0; 162 // Filter options used for displayed list of applications 163 public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0; 164 public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1; 165 public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2; 166 public static final int FILTER_APPS_DISABLED = MENU_OPTIONS_BASE + 3; 167 168 public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4; 169 public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5; 170 public static final int SHOW_RUNNING_SERVICES = MENU_OPTIONS_BASE + 6; 171 public static final int SHOW_BACKGROUND_PROCESSES = MENU_OPTIONS_BASE + 7; 172 public static final int RESET_APP_PREFERENCES = MENU_OPTIONS_BASE + 8; 173 // sort order 174 private int mSortOrder = SORT_ORDER_ALPHA; 175 176 private ApplicationsState mApplicationsState; 177 178 public static class TabInfo implements OnItemClickListener { 179 public final ManageApplications mOwner; 180 public final ApplicationsState mApplicationsState; 181 public final CharSequence mLabel; 182 public final int mListType; 183 public final int mFilter; 184 public final AppClickListener mClickListener; 185 public final CharSequence mInvalidSizeStr; 186 public final CharSequence mComputingSizeStr; 187 private final Bundle mSavedInstanceState; 188 189 public ApplicationsAdapter mApplications; 190 public LayoutInflater mInflater; 191 public View mRootView; 192 193 private IMediaContainerService mContainerService; 194 195 private View mLoadingContainer; 196 197 private View mListContainer; 198 199 // ListView used to display list 200 private ListView mListView; 201 // Custom view used to display running processes 202 private RunningProcessesView mRunningProcessesView; 203 204 //private LinearColorBar mColorBar; 205 //private TextView mStorageChartLabel; 206 //private TextView mUsedStorageText; 207 //private TextView mFreeStorageText; 208 private long mFreeStorage = 0, mAppStorage = 0, mTotalStorage = 0; 209 private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage; 210 211 final Runnable mRunningProcessesAvail = new Runnable() { 212 public void run() { 213 handleRunningProcessesAvail(); 214 } 215 }; 216 217 public TabInfo(ManageApplications owner, ApplicationsState apps, 218 CharSequence label, int listType, AppClickListener clickListener, 219 Bundle savedInstanceState) { 220 mOwner = owner; 221 mApplicationsState = apps; 222 mLabel = label; 223 mListType = listType; 224 switch (listType) { 225 case LIST_TYPE_DOWNLOADED: mFilter = FILTER_APPS_THIRD_PARTY; break; 226 case LIST_TYPE_SDCARD: mFilter = FILTER_APPS_SDCARD; break; 227 case LIST_TYPE_DISABLED: mFilter = FILTER_APPS_DISABLED; break; 228 default: mFilter = FILTER_APPS_ALL; break; 229 } 230 mClickListener = clickListener; 231 mInvalidSizeStr = owner.getActivity().getText(R.string.invalid_size_value); 232 mComputingSizeStr = owner.getActivity().getText(R.string.computing_size); 233 mSavedInstanceState = savedInstanceState; 234 } 235 236 public void setContainerService(IMediaContainerService containerService) { 237 mContainerService = containerService; 238 updateStorageUsage(); 239 } 240 241 public View build(LayoutInflater inflater, ViewGroup contentParent, View contentChild) { 242 if (mRootView != null) { 243 return mRootView; 244 } 245 mInflater = inflater; 246 mRootView = inflater.inflate(mListType == LIST_TYPE_RUNNING 247 ? R.layout.manage_applications_running 248 : R.layout.manage_applications_apps, null); 249 mLoadingContainer = mRootView.findViewById(R.id.loading_container); 250 mLoadingContainer.setVisibility(View.VISIBLE); 251 mListContainer = mRootView.findViewById(R.id.list_container); 252 if (mListContainer != null) { 253 // Create adapter and list view here 254 View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty); 255 ListView lv = (ListView) mListContainer.findViewById(android.R.id.list); 256 if (emptyView != null) { 257 lv.setEmptyView(emptyView); 258 } 259 lv.setOnItemClickListener(this); 260 lv.setSaveEnabled(true); 261 lv.setItemsCanFocus(true); 262 lv.setTextFilterEnabled(true); 263 mListView = lv; 264 mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter); 265 mListView.setAdapter(mApplications); 266 mListView.setRecyclerListener(mApplications); 267 //mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar); 268 //mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel); 269 //mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText); 270 //mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText); 271 Utils.prepareCustomPreferencesList(contentParent, contentChild, mListView, false); 272 if (mFilter == FILTER_APPS_SDCARD) { 273 //mStorageChartLabel.setText(mOwner.getActivity().getText( 274 // R.string.sd_card_storage)); 275 } else { 276 //mStorageChartLabel.setText(mOwner.getActivity().getText( 277 // R.string.internal_storage)); 278 } 279 applyCurrentStorage(); 280 } 281 mRunningProcessesView = (RunningProcessesView)mRootView.findViewById( 282 R.id.running_processes); 283 if (mRunningProcessesView != null) { 284 mRunningProcessesView.doCreate(mSavedInstanceState); 285 } 286 287 return mRootView; 288 } 289 290 public void detachView() { 291 if (mRootView != null) { 292 ViewGroup group = (ViewGroup)mRootView.getParent(); 293 if (group != null) { 294 group.removeView(mRootView); 295 } 296 } 297 } 298 299 public void resume(int sortOrder) { 300 if (mApplications != null) { 301 mApplications.resume(sortOrder); 302 } 303 if (mRunningProcessesView != null) { 304 boolean haveData = mRunningProcessesView.doResume(mOwner, mRunningProcessesAvail); 305 if (haveData) { 306 mRunningProcessesView.setVisibility(View.VISIBLE); 307 mLoadingContainer.setVisibility(View.INVISIBLE); 308 } else { 309 mLoadingContainer.setVisibility(View.VISIBLE); 310 } 311 } 312 } 313 314 public void pause() { 315 if (mApplications != null) { 316 mApplications.pause(); 317 } 318 if (mRunningProcessesView != null) { 319 mRunningProcessesView.doPause(); 320 } 321 } 322 323 public void release() { 324 if (mApplications != null) { 325 mApplications.release(); 326 } 327 } 328 329 void updateStorageUsage() { 330 // Make sure a callback didn't come at an inopportune time. 331 if (mOwner.getActivity() == null) return; 332 // Doesn't make sense for stuff that is not an app list. 333 if (mApplications == null) return; 334 335 mFreeStorage = 0; 336 mAppStorage = 0; 337 mTotalStorage = 0; 338 339 if (mFilter == FILTER_APPS_SDCARD) { 340 if (mContainerService != null) { 341 try { 342 final long[] stats = mContainerService.getFileSystemStats( 343 Environment.getExternalStorageDirectory().getPath()); 344 mTotalStorage = stats[0]; 345 mFreeStorage = stats[1]; 346 } catch (RemoteException e) { 347 Log.w(TAG, "Problem in container service", e); 348 } 349 } 350 351 if (mApplications != null) { 352 final int N = mApplications.getCount(); 353 for (int i=0; i<N; i++) { 354 ApplicationsState.AppEntry ae = mApplications.getAppEntry(i); 355 mAppStorage += ae.externalCodeSize + ae.externalDataSize 356 + ae.externalCacheSize; 357 } 358 } 359 } else { 360 if (mContainerService != null) { 361 try { 362 final long[] stats = mContainerService.getFileSystemStats( 363 Environment.getDataDirectory().getPath()); 364 mTotalStorage = stats[0]; 365 mFreeStorage = stats[1]; 366 } catch (RemoteException e) { 367 Log.w(TAG, "Problem in container service", e); 368 } 369 } 370 371 final boolean emulatedStorage = Environment.isExternalStorageEmulated(); 372 if (mApplications != null) { 373 final int N = mApplications.getCount(); 374 for (int i=0; i<N; i++) { 375 ApplicationsState.AppEntry ae = mApplications.getAppEntry(i); 376 mAppStorage += ae.codeSize + ae.dataSize; 377 if (emulatedStorage) { 378 mAppStorage += ae.externalCodeSize + ae.externalDataSize; 379 } 380 } 381 } 382 mFreeStorage += mApplicationsState.sumCacheSizes(); 383 } 384 385 applyCurrentStorage(); 386 } 387 388 void applyCurrentStorage() { 389 // If view hierarchy is not yet created, no views to update. 390 if (mRootView == null) { 391 return; 392 } 393 /* 394 if (mTotalStorage > 0) { 395 BidiFormatter bidiFormatter = BidiFormatter.getInstance(); 396 mColorBar.setRatios((mTotalStorage-mFreeStorage-mAppStorage)/(float)mTotalStorage, 397 mAppStorage/(float)mTotalStorage, mFreeStorage/(float)mTotalStorage); 398 long usedStorage = mTotalStorage - mFreeStorage; 399 if (mLastUsedStorage != usedStorage) { 400 mLastUsedStorage = usedStorage; 401 String sizeStr = bidiFormatter.unicodeWrap( 402 Formatter.formatShortFileSize(mOwner.getActivity(), usedStorage)); 403 mUsedStorageText.setText(mOwner.getActivity().getResources().getString( 404 R.string.service_foreground_processes, sizeStr)); 405 } 406 if (mLastFreeStorage != mFreeStorage) { 407 mLastFreeStorage = mFreeStorage; 408 String sizeStr = bidiFormatter.unicodeWrap( 409 Formatter.formatShortFileSize(mOwner.getActivity(), mFreeStorage)); 410 mFreeStorageText.setText(mOwner.getActivity().getResources().getString( 411 R.string.service_background_processes, sizeStr)); 412 } 413 } else { 414 mColorBar.setRatios(0, 0, 0); 415 if (mLastUsedStorage != -1) { 416 mLastUsedStorage = -1; 417 mUsedStorageText.setText(""); 418 } 419 if (mLastFreeStorage != -1) { 420 mLastFreeStorage = -1; 421 mFreeStorageText.setText(""); 422 } 423 } 424 */ 425 } 426 427 @Override 428 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 429 mClickListener.onItemClick(this, parent, view, position, id); 430 } 431 432 void handleRunningProcessesAvail() { 433 mLoadingContainer.startAnimation(AnimationUtils.loadAnimation( 434 mOwner.getActivity(), android.R.anim.fade_out)); 435 mRunningProcessesView.startAnimation(AnimationUtils.loadAnimation( 436 mOwner.getActivity(), android.R.anim.fade_in)); 437 mRunningProcessesView.setVisibility(View.VISIBLE); 438 mLoadingContainer.setVisibility(View.GONE); 439 } 440 } 441 private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); 442 private int mNumTabs; 443 TabInfo mCurTab = null; 444 445 // Size resource used for packages whose size computation failed for some reason 446 CharSequence mInvalidSizeStr; 447 private CharSequence mComputingSizeStr; 448 449 // layout inflater object used to inflate views 450 private LayoutInflater mInflater; 451 452 private String mCurrentPkgName; 453 private int mCurrentUid; 454 455 private Menu mOptionsMenu; 456 457 // These are for keeping track of activity and spinner switch state. 458 private boolean mActivityResumed; 459 460 private static final int LIST_TYPE_MISSING = -1; 461 static final int LIST_TYPE_DOWNLOADED = 0; 462 static final int LIST_TYPE_RUNNING = 1; 463 static final int LIST_TYPE_SDCARD = 2; 464 static final int LIST_TYPE_ALL = 3; 465 static final int LIST_TYPE_DISABLED = 4; 466 467 private boolean mShowBackground = false; 468 469 private int mDefaultListType = -1; 470 471 private ViewGroup mContentContainer; 472 private View mRootView; 473 private ViewPager mViewPager; 474 private ViewGroup mPinnedHeader; 475 private Spinner mSpinner; 476 private Context mContext; 477 478 AlertDialog mResetDialog; 479 480 class MyPagerAdapter extends PagerAdapter 481 implements ViewPager.OnPageChangeListener { 482 int mCurPos = 0; 483 484 @Override 485 public int getCount() { 486 return mNumTabs; 487 } 488 489 @Override 490 public Object instantiateItem(ViewGroup container, int position) { 491 TabInfo tab = mTabs.get(position); 492 View root = tab.build(mInflater, mContentContainer, mRootView); 493 container.addView(root); 494 root.setTag(R.id.name, tab); 495 return root; 496 } 497 498 @Override 499 public void destroyItem(ViewGroup container, int position, Object object) { 500 container.removeView((View)object); 501 } 502 503 @Override 504 public boolean isViewFromObject(View view, Object object) { 505 return view == object; 506 } 507 508 @Override 509 public int getItemPosition(Object object) { 510 return super.getItemPosition(object); 511 //return ((TabInfo)((View)object).getTag(R.id.name)).mListType; 512 } 513 514 @Override 515 public CharSequence getPageTitle(int position) { 516 return mTabs.get(position).mLabel; 517 } 518 519 @Override 520 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 521 } 522 523 @Override 524 public void onPageSelected(int position) { 525 mCurPos = position; 526 } 527 528 @Override 529 public void onPageScrollStateChanged(int state) { 530 if (state == ViewPager.SCROLL_STATE_IDLE) { 531 updateCurrentTab(mCurPos); 532 } 533 } 534 } 535 536 /* 537 * Custom adapter implementation for the ListView 538 * This adapter maintains a map for each displayed application and its properties 539 * An index value on each AppInfo object indicates the correct position or index 540 * in the list. If the list gets updated dynamically when the user is viewing the list of 541 * applications, we need to return the correct index of position. This is done by mapping 542 * the getId methods via the package name into the internal maps and indices. 543 * The order of applications in the list is mirrored in mAppLocalList 544 */ 545 static class ApplicationsAdapter extends BaseAdapter implements Filterable, 546 ApplicationsState.Callbacks, AbsListView.RecyclerListener { 547 private final ApplicationsState mState; 548 private final ApplicationsState.Session mSession; 549 private final TabInfo mTab; 550 private final Context mContext; 551 private final ArrayList<View> mActive = new ArrayList<View>(); 552 private final int mFilterMode; 553 private ArrayList<ApplicationsState.AppEntry> mBaseEntries; 554 private ArrayList<ApplicationsState.AppEntry> mEntries; 555 private boolean mResumed; 556 private int mLastSortMode=-1; 557 private boolean mWaitingForData; 558 private int mWhichSize = SIZE_TOTAL; 559 CharSequence mCurFilterPrefix; 560 561 private Filter mFilter = new Filter() { 562 @Override 563 protected FilterResults performFiltering(CharSequence constraint) { 564 ArrayList<ApplicationsState.AppEntry> entries 565 = applyPrefixFilter(constraint, mBaseEntries); 566 FilterResults fr = new FilterResults(); 567 fr.values = entries; 568 fr.count = entries.size(); 569 return fr; 570 } 571 572 @Override 573 protected void publishResults(CharSequence constraint, FilterResults results) { 574 mCurFilterPrefix = constraint; 575 mEntries = (ArrayList<ApplicationsState.AppEntry>)results.values; 576 notifyDataSetChanged(); 577 mTab.updateStorageUsage(); 578 } 579 }; 580 581 public ApplicationsAdapter(ApplicationsState state, TabInfo tab, int filterMode) { 582 mState = state; 583 mSession = state.newSession(this); 584 mTab = tab; 585 mContext = tab.mOwner.getActivity(); 586 mFilterMode = filterMode; 587 } 588 589 public void resume(int sort) { 590 if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed); 591 if (!mResumed) { 592 mResumed = true; 593 mSession.resume(); 594 mLastSortMode = sort; 595 rebuild(true); 596 } else { 597 rebuild(sort); 598 } 599 } 600 601 public void pause() { 602 if (mResumed) { 603 mResumed = false; 604 mSession.pause(); 605 } 606 } 607 608 public void release() { 609 mSession.release(); 610 } 611 612 public void rebuild(int sort) { 613 if (sort == mLastSortMode) { 614 return; 615 } 616 mLastSortMode = sort; 617 rebuild(true); 618 } 619 620 public void rebuild(boolean eraseold) { 621 if (DEBUG) Log.i(TAG, "Rebuilding app list..."); 622 ApplicationsState.AppFilter filterObj; 623 Comparator<AppEntry> comparatorObj; 624 boolean emulated = Environment.isExternalStorageEmulated(); 625 if (emulated) { 626 mWhichSize = SIZE_TOTAL; 627 } else { 628 mWhichSize = SIZE_INTERNAL; 629 } 630 switch (mFilterMode) { 631 case FILTER_APPS_THIRD_PARTY: 632 filterObj = ApplicationsState.THIRD_PARTY_FILTER; 633 break; 634 case FILTER_APPS_SDCARD: 635 filterObj = ApplicationsState.ON_SD_CARD_FILTER; 636 if (!emulated) { 637 mWhichSize = SIZE_EXTERNAL; 638 } 639 break; 640 case FILTER_APPS_DISABLED: 641 filterObj = ApplicationsState.DISABLED_FILTER; 642 break; 643 default: 644 filterObj = ApplicationsState.ALL_ENABLED_FILTER; 645 break; 646 } 647 switch (mLastSortMode) { 648 case SORT_ORDER_SIZE: 649 switch (mWhichSize) { 650 case SIZE_INTERNAL: 651 comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR; 652 break; 653 case SIZE_EXTERNAL: 654 comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR; 655 break; 656 default: 657 comparatorObj = ApplicationsState.SIZE_COMPARATOR; 658 break; 659 } 660 break; 661 default: 662 comparatorObj = ApplicationsState.ALPHA_COMPARATOR; 663 break; 664 } 665 ArrayList<ApplicationsState.AppEntry> entries 666 = mSession.rebuild(filterObj, comparatorObj); 667 if (entries == null && !eraseold) { 668 // Don't have new list yet, but can continue using the old one. 669 return; 670 } 671 mBaseEntries = entries; 672 if (mBaseEntries != null) { 673 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries); 674 } else { 675 mEntries = null; 676 } 677 notifyDataSetChanged(); 678 mTab.updateStorageUsage(); 679 680 if (entries == null) { 681 mWaitingForData = true; 682 mTab.mListContainer.setVisibility(View.INVISIBLE); 683 mTab.mLoadingContainer.setVisibility(View.VISIBLE); 684 } else { 685 mTab.mListContainer.setVisibility(View.VISIBLE); 686 mTab.mLoadingContainer.setVisibility(View.GONE); 687 } 688 } 689 690 ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix, 691 ArrayList<ApplicationsState.AppEntry> origEntries) { 692 if (prefix == null || prefix.length() == 0) { 693 return origEntries; 694 } else { 695 String prefixStr = ApplicationsState.normalize(prefix.toString()); 696 final String spacePrefixStr = " " + prefixStr; 697 ArrayList<ApplicationsState.AppEntry> newEntries 698 = new ArrayList<ApplicationsState.AppEntry>(); 699 for (int i=0; i<origEntries.size(); i++) { 700 ApplicationsState.AppEntry entry = origEntries.get(i); 701 String nlabel = entry.getNormalizedLabel(); 702 if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) { 703 newEntries.add(entry); 704 } 705 } 706 return newEntries; 707 } 708 } 709 710 @Override 711 public void onRunningStateChanged(boolean running) { 712 mTab.mOwner.getActivity().setProgressBarIndeterminateVisibility(running); 713 } 714 715 @Override 716 public void onRebuildComplete(ArrayList<AppEntry> apps) { 717 if (mTab.mLoadingContainer.getVisibility() == View.VISIBLE) { 718 mTab.mLoadingContainer.startAnimation(AnimationUtils.loadAnimation( 719 mContext, android.R.anim.fade_out)); 720 mTab.mListContainer.startAnimation(AnimationUtils.loadAnimation( 721 mContext, android.R.anim.fade_in)); 722 } 723 mTab.mListContainer.setVisibility(View.VISIBLE); 724 mTab.mLoadingContainer.setVisibility(View.GONE); 725 mWaitingForData = false; 726 mBaseEntries = apps; 727 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries); 728 notifyDataSetChanged(); 729 mTab.updateStorageUsage(); 730 } 731 732 @Override 733 public void onPackageListChanged() { 734 rebuild(false); 735 } 736 737 @Override 738 public void onPackageIconChanged() { 739 // We ensure icons are loaded when their item is displayed, so 740 // don't care about icons loaded in the background. 741 } 742 743 @Override 744 public void onPackageSizeChanged(String packageName) { 745 for (int i=0; i<mActive.size(); i++) { 746 AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag(); 747 if (holder.entry.info.packageName.equals(packageName)) { 748 synchronized (holder.entry) { 749 holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize); 750 } 751 if (holder.entry.info.packageName.equals(mTab.mOwner.mCurrentPkgName) 752 && mLastSortMode == SORT_ORDER_SIZE) { 753 // We got the size information for the last app the 754 // user viewed, and are sorting by size... they may 755 // have cleared data, so we immediately want to resort 756 // the list with the new size to reflect it to the user. 757 rebuild(false); 758 } 759 mTab.updateStorageUsage(); 760 return; 761 } 762 } 763 } 764 765 @Override 766 public void onAllSizesComputed() { 767 if (mLastSortMode == SORT_ORDER_SIZE) { 768 rebuild(false); 769 } 770 mTab.updateStorageUsage(); 771 } 772 773 public int getCount() { 774 return mEntries != null ? mEntries.size() : 0; 775 } 776 777 public Object getItem(int position) { 778 return mEntries.get(position); 779 } 780 781 public ApplicationsState.AppEntry getAppEntry(int position) { 782 return mEntries.get(position); 783 } 784 785 public long getItemId(int position) { 786 return mEntries.get(position).id; 787 } 788 789 public View getView(int position, View convertView, ViewGroup parent) { 790 // A ViewHolder keeps references to children views to avoid unnecessary calls 791 // to findViewById() on each row. 792 AppViewHolder holder = AppViewHolder.createOrRecycle(mTab.mInflater, convertView); 793 convertView = holder.rootView; 794 795 // Bind the data efficiently with the holder 796 ApplicationsState.AppEntry entry = mEntries.get(position); 797 synchronized (entry) { 798 holder.entry = entry; 799 if (entry.label != null) { 800 holder.appName.setText(entry.label); 801 } 802 mState.ensureIcon(entry); 803 if (entry.icon != null) { 804 holder.appIcon.setImageDrawable(entry.icon); 805 } 806 holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize); 807 if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { 808 holder.disabled.setVisibility(View.VISIBLE); 809 holder.disabled.setText(R.string.not_installed); 810 } else if (!entry.info.enabled) { 811 holder.disabled.setVisibility(View.VISIBLE); 812 holder.disabled.setText(R.string.disabled); 813 } else { 814 holder.disabled.setVisibility(View.GONE); 815 } 816 if (mFilterMode == FILTER_APPS_SDCARD) { 817 holder.checkBox.setVisibility(View.VISIBLE); 818 holder.checkBox.setChecked((entry.info.flags 819 & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); 820 } else { 821 holder.checkBox.setVisibility(View.GONE); 822 } 823 } 824 mActive.remove(convertView); 825 mActive.add(convertView); 826 return convertView; 827 } 828 829 @Override 830 public Filter getFilter() { 831 return mFilter; 832 } 833 834 @Override 835 public void onMovedToScrapHeap(View view) { 836 mActive.remove(view); 837 } 838 } 839 840 @Override 841 public void onCreate(Bundle savedInstanceState) { 842 super.onCreate(savedInstanceState); 843 844 setHasOptionsMenu(true); 845 846 mContext = getActivity(); 847 mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); 848 Intent intent = getActivity().getIntent(); 849 String action = intent.getAction(); 850 int defaultListType = LIST_TYPE_DOWNLOADED; 851 String className = getArguments() != null 852 ? getArguments().getString("classname") : null; 853 if (className == null) { 854 className = intent.getComponent().getClassName(); 855 } 856 if (className.equals(RunningServicesActivity.class.getName()) 857 || className.endsWith(".RunningServices")) { 858 defaultListType = LIST_TYPE_RUNNING; 859 } else if (className.equals(StorageUseActivity.class.getName()) 860 || Intent.ACTION_MANAGE_PACKAGE_STORAGE.equals(action) 861 || className.endsWith(".StorageUse")) { 862 mSortOrder = SORT_ORDER_SIZE; 863 defaultListType = LIST_TYPE_ALL; 864 } else if (android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) { 865 // Select the all-apps list, with the default sorting 866 defaultListType = LIST_TYPE_ALL; 867 } 868 869 if (savedInstanceState != null) { 870 mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder); 871 int tmp = savedInstanceState.getInt(EXTRA_DEFAULT_LIST_TYPE, -1); 872 if (tmp != -1) defaultListType = tmp; 873 mShowBackground = savedInstanceState.getBoolean(EXTRA_SHOW_BACKGROUND, false); 874 } 875 876 mDefaultListType = defaultListType; 877 878 final Intent containerIntent = new Intent().setComponent( 879 StorageMeasurement.DEFAULT_CONTAINER_COMPONENT); 880 getActivity().bindService(containerIntent, mContainerConnection, Context.BIND_AUTO_CREATE); 881 882 mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value); 883 mComputingSizeStr = getActivity().getText(R.string.computing_size); 884 885 TabInfo tab = new TabInfo(this, mApplicationsState, 886 getActivity().getString(R.string.filter_apps_third_party), 887 LIST_TYPE_DOWNLOADED, this, savedInstanceState); 888 mTabs.add(tab); 889 890 if (!Environment.isExternalStorageEmulated()) { 891 tab = new TabInfo(this, mApplicationsState, 892 getActivity().getString(R.string.filter_apps_onsdcard), 893 LIST_TYPE_SDCARD, this, savedInstanceState); 894 mTabs.add(tab); 895 } 896 897 tab = new TabInfo(this, mApplicationsState, 898 getActivity().getString(R.string.filter_apps_running), 899 LIST_TYPE_RUNNING, this, savedInstanceState); 900 mTabs.add(tab); 901 902 tab = new TabInfo(this, mApplicationsState, 903 getActivity().getString(R.string.filter_apps_all), 904 LIST_TYPE_ALL, this, savedInstanceState); 905 mTabs.add(tab); 906 907 tab = new TabInfo(this, mApplicationsState, 908 getActivity().getString(R.string.filter_apps_disabled), 909 LIST_TYPE_DISABLED, this, savedInstanceState); 910 mTabs.add(tab); 911 912 mNumTabs = mTabs.size(); 913 } 914 915 916 @Override 917 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 918 // initialize the inflater 919 mInflater = inflater; 920 921 View rootView = mInflater.inflate(R.layout.manage_applications_content, 922 container, false); 923 mContentContainer = container; 924 mRootView = rootView; 925 mViewPager = (ViewPager) rootView.findViewById(R.id.pager); 926 MyPagerAdapter adapter = new MyPagerAdapter(); 927 mViewPager.setAdapter(adapter); 928 mViewPager.setOnPageChangeListener(adapter); 929 PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs); 930 tabs.setTabIndicatorColorResource(R.color.theme_accent); 931 932 // We have to do this now because PreferenceFrameLayout looks at it 933 // only when the view is added. 934 if (container instanceof PreferenceFrameLayout) { 935 ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true; 936 } 937 938 if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_RESET_DIALOG)) { 939 buildResetDialog(); 940 } 941 942 if (savedInstanceState == null) { 943 // First time init: make sure view pager is showing the correct tab. 944 int extraCurrentListType = getActivity().getIntent().getIntExtra(EXTRA_LIST_TYPE, 945 LIST_TYPE_MISSING); 946 int currentListType = (extraCurrentListType != LIST_TYPE_MISSING) 947 ? extraCurrentListType : mDefaultListType; 948 for (int i = 0; i < mNumTabs; i++) { 949 TabInfo tab = mTabs.get(i); 950 if (tab.mListType == currentListType) { 951 mViewPager.setCurrentItem(i); 952 break; 953 } 954 } 955 } 956 957 return rootView; 958 } 959 960 @Override 961 public void onStart() { 962 super.onStart(); 963 } 964 965 @Override 966 public void onResume() { 967 super.onResume(); 968 mActivityResumed = true; 969 updateCurrentTab(mViewPager.getCurrentItem()); 970 updateNumTabs(); 971 updateOptionsMenu(); 972 } 973 974 @Override 975 public void onSaveInstanceState(Bundle outState) { 976 super.onSaveInstanceState(outState); 977 outState.putInt(EXTRA_SORT_ORDER, mSortOrder); 978 if (mDefaultListType != -1) { 979 outState.putInt(EXTRA_DEFAULT_LIST_TYPE, mDefaultListType); 980 } 981 outState.putBoolean(EXTRA_SHOW_BACKGROUND, mShowBackground); 982 if (mResetDialog != null) { 983 outState.putBoolean(EXTRA_RESET_DIALOG, true); 984 } 985 } 986 987 @Override 988 public void onPause() { 989 super.onPause(); 990 mActivityResumed = false; 991 for (int i=0; i<mTabs.size(); i++) { 992 mTabs.get(i).pause(); 993 } 994 } 995 996 @Override 997 public void onStop() { 998 super.onStop(); 999 if (mResetDialog != null) { 1000 mResetDialog.dismiss(); 1001 mResetDialog = null; 1002 } 1003 } 1004 1005 @Override 1006 public void onDestroyView() { 1007 super.onDestroyView(); 1008 1009 // We are going to keep the tab data structures around, but they 1010 // are no longer attached to their view hierarchy. 1011 for (int i=0; i<mTabs.size(); i++) { 1012 mTabs.get(i).detachView(); 1013 mTabs.get(i).release(); 1014 } 1015 } 1016 1017 @Override 1018 public void onActivityResult(int requestCode, int resultCode, Intent data) { 1019 if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { 1020 mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid)); 1021 } 1022 } 1023 1024 private void updateNumTabs() { 1025 int newNum = mApplicationsState.haveDisabledApps() ? mTabs.size() : (mTabs.size()-1); 1026 if (newNum != mNumTabs) { 1027 mNumTabs = newNum; 1028 if (mViewPager != null) { 1029 mViewPager.getAdapter().notifyDataSetChanged(); 1030 } 1031 } 1032 } 1033 1034 TabInfo tabForType(int type) { 1035 for (int i = 0; i < mTabs.size(); i++) { 1036 TabInfo tab = mTabs.get(i); 1037 if (tab.mListType == type) { 1038 return tab; 1039 } 1040 } 1041 return null; 1042 } 1043 1044 // utility method used to start sub activity 1045 private void startApplicationDetailsActivity() { 1046 // TODO: Figure out if there is a way where we can spin up the profile's settings 1047 // process ahead of time, to avoid a long load of data when user clicks on a managed app. 1048 // Maybe when they load the list of apps that contains managed profile apps. 1049 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 1050 intent.setData(Uri.fromParts("package", mCurrentPkgName, null)); 1051 getActivity().startActivityAsUser(intent, 1052 new UserHandle(UserHandle.getUserId(mCurrentUid))); 1053 } 1054 1055 @Override 1056 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 1057 mOptionsMenu = menu; 1058 // note: icons removed for now because the cause the new action 1059 // bar UI to be very confusing. 1060 menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha) 1061 //.setIcon(android.R.drawable.ic_menu_sort_alphabetically) 1062 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 1063 menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size) 1064 //.setIcon(android.R.drawable.ic_menu_sort_by_size) 1065 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 1066 menu.add(0, SHOW_RUNNING_SERVICES, 3, R.string.show_running_services) 1067 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 1068 menu.add(0, SHOW_BACKGROUND_PROCESSES, 3, R.string.show_background_processes) 1069 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 1070 menu.add(0, RESET_APP_PREFERENCES, 4, R.string.reset_app_preferences) 1071 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 1072 updateOptionsMenu(); 1073 } 1074 1075 @Override 1076 public void onPrepareOptionsMenu(Menu menu) { 1077 updateOptionsMenu(); 1078 } 1079 1080 @Override 1081 public void onDestroyOptionsMenu() { 1082 mOptionsMenu = null; 1083 } 1084 1085 @Override 1086 public void onDestroy() { 1087 getActivity().unbindService(mContainerConnection); 1088 super.onDestroy(); 1089 } 1090 1091 void updateOptionsMenu() { 1092 if (mOptionsMenu == null) { 1093 return; 1094 } 1095 1096 /* 1097 * The running processes screen doesn't use the mApplicationsAdapter 1098 * so bringing up this menu in that case doesn't make any sense. 1099 */ 1100 if (mCurTab != null && mCurTab.mListType == LIST_TYPE_RUNNING) { 1101 TabInfo tab = tabForType(LIST_TYPE_RUNNING); 1102 boolean showingBackground = tab != null && tab.mRunningProcessesView != null 1103 ? tab.mRunningProcessesView.mAdapter.getShowBackground() : false; 1104 mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(false); 1105 mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(false); 1106 mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(showingBackground); 1107 mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(!showingBackground); 1108 mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(false); 1109 mShowBackground = showingBackground; 1110 } else { 1111 mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA); 1112 mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE); 1113 mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(false); 1114 mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(false); 1115 mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(true); 1116 } 1117 } 1118 1119 void buildResetDialog() { 1120 if (mResetDialog == null) { 1121 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 1122 builder.setTitle(R.string.reset_app_preferences_title); 1123 builder.setMessage(R.string.reset_app_preferences_desc); 1124 builder.setPositiveButton(R.string.reset_app_preferences_button, this); 1125 builder.setNegativeButton(R.string.cancel, null); 1126 mResetDialog = builder.show(); 1127 mResetDialog.setOnDismissListener(this); 1128 } 1129 } 1130 1131 @Override 1132 public void onDismiss(DialogInterface dialog) { 1133 if (mResetDialog == dialog) { 1134 mResetDialog = null; 1135 } 1136 } 1137 1138 1139 @Override 1140 public void onClick(DialogInterface dialog, int which) { 1141 if (mResetDialog == dialog) { 1142 final PackageManager pm = getActivity().getPackageManager(); 1143 final IPackageManager mIPm = IPackageManager.Stub.asInterface( 1144 ServiceManager.getService("package")); 1145 final INotificationManager nm = INotificationManager.Stub.asInterface( 1146 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 1147 final NetworkPolicyManager npm = NetworkPolicyManager.from(getActivity()); 1148 final AppOpsManager aom = (AppOpsManager)getActivity().getSystemService( 1149 Context.APP_OPS_SERVICE); 1150 final Handler handler = new Handler(getActivity().getMainLooper()); 1151 (new AsyncTask<Void, Void, Void>() { 1152 @Override protected Void doInBackground(Void... params) { 1153 List<ApplicationInfo> apps = pm.getInstalledApplications( 1154 PackageManager.GET_DISABLED_COMPONENTS); 1155 for (int i=0; i<apps.size(); i++) { 1156 ApplicationInfo app = apps.get(i); 1157 try { 1158 if (DEBUG) Log.v(TAG, "Enabling notifications: " + app.packageName); 1159 nm.setNotificationsEnabledForPackage(app.packageName, app.uid, true); 1160 } catch (android.os.RemoteException ex) { 1161 } 1162 if (!app.enabled) { 1163 if (DEBUG) Log.v(TAG, "Enabling app: " + app.packageName); 1164 if (pm.getApplicationEnabledSetting(app.packageName) 1165 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { 1166 pm.setApplicationEnabledSetting(app.packageName, 1167 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 1168 PackageManager.DONT_KILL_APP); 1169 } 1170 } 1171 } 1172 try { 1173 mIPm.resetPreferredActivities(UserHandle.myUserId()); 1174 } catch (RemoteException e) { 1175 } 1176 aom.resetAllModes(); 1177 final int[] restrictedUids = npm.getUidsWithPolicy( 1178 POLICY_REJECT_METERED_BACKGROUND); 1179 final int currentUserId = ActivityManager.getCurrentUser(); 1180 for (int uid : restrictedUids) { 1181 // Only reset for current user 1182 if (UserHandle.getUserId(uid) == currentUserId) { 1183 if (DEBUG) Log.v(TAG, "Clearing data policy: " + uid); 1184 npm.setUidPolicy(uid, POLICY_NONE); 1185 } 1186 } 1187 handler.post(new Runnable() { 1188 @Override public void run() { 1189 if (DEBUG) Log.v(TAG, "Done clearing"); 1190 if (getActivity() != null && mActivityResumed) { 1191 if (DEBUG) Log.v(TAG, "Updating UI!"); 1192 for (int i=0; i<mTabs.size(); i++) { 1193 TabInfo tab = mTabs.get(i); 1194 if (tab.mApplications != null) { 1195 tab.mApplications.pause(); 1196 } 1197 } 1198 if (mCurTab != null) { 1199 mCurTab.resume(mSortOrder); 1200 } 1201 } 1202 } 1203 }); 1204 return null; 1205 } 1206 }).execute(); 1207 } 1208 } 1209 1210 @Override 1211 public boolean onOptionsItemSelected(MenuItem item) { 1212 int menuId = item.getItemId(); 1213 if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) { 1214 mSortOrder = menuId; 1215 if (mCurTab != null && mCurTab.mApplications != null) { 1216 mCurTab.mApplications.rebuild(mSortOrder); 1217 } 1218 } else if (menuId == SHOW_RUNNING_SERVICES) { 1219 mShowBackground = false; 1220 if (mCurTab != null && mCurTab.mRunningProcessesView != null) { 1221 mCurTab.mRunningProcessesView.mAdapter.setShowBackground(false); 1222 } 1223 } else if (menuId == SHOW_BACKGROUND_PROCESSES) { 1224 mShowBackground = true; 1225 if (mCurTab != null && mCurTab.mRunningProcessesView != null) { 1226 mCurTab.mRunningProcessesView.mAdapter.setShowBackground(true); 1227 } 1228 } else if (menuId == RESET_APP_PREFERENCES) { 1229 buildResetDialog(); 1230 } else { 1231 // Handle the home button 1232 return false; 1233 } 1234 updateOptionsMenu(); 1235 return true; 1236 } 1237 1238 public void onItemClick(TabInfo tab, AdapterView<?> parent, View view, int position, 1239 long id) { 1240 if (tab.mApplications != null && tab.mApplications.getCount() > position) { 1241 ApplicationsState.AppEntry entry = tab.mApplications.getAppEntry(position); 1242 mCurrentPkgName = entry.info.packageName; 1243 mCurrentUid = entry.info.uid; 1244 startApplicationDetailsActivity(); 1245 } 1246 } 1247 1248 public void updateCurrentTab(int position) { 1249 TabInfo tab = mTabs.get(position); 1250 mCurTab = tab; 1251 1252 // Put things in the correct paused/resumed state. 1253 if (mActivityResumed) { 1254 mCurTab.build(mInflater, mContentContainer, mRootView); 1255 mCurTab.resume(mSortOrder); 1256 } else { 1257 mCurTab.pause(); 1258 } 1259 for (int i=0; i<mTabs.size(); i++) { 1260 TabInfo t = mTabs.get(i); 1261 if (t != mCurTab) { 1262 t.pause(); 1263 } 1264 } 1265 1266 mCurTab.updateStorageUsage(); 1267 updateOptionsMenu(); 1268 final Activity host = getActivity(); 1269 if (host != null) { 1270 host.invalidateOptionsMenu(); 1271 } 1272 } 1273 1274 private volatile IMediaContainerService mContainerService; 1275 1276 private final ServiceConnection mContainerConnection = new ServiceConnection() { 1277 @Override 1278 public void onServiceConnected(ComponentName name, IBinder service) { 1279 mContainerService = IMediaContainerService.Stub.asInterface(service); 1280 for (int i=0; i<mTabs.size(); i++) { 1281 mTabs.get(i).setContainerService(mContainerService); 1282 } 1283 } 1284 1285 @Override 1286 public void onServiceDisconnected(ComponentName name) { 1287 mContainerService = null; 1288 } 1289 }; 1290} 1291