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