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