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.Fragment; 26import android.app.INotificationManager; 27import android.content.ComponentName; 28import android.content.Context; 29import android.content.DialogInterface; 30import android.content.Intent; 31import android.content.IntentFilter; 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.os.AsyncTask; 39import android.os.Bundle; 40import android.os.Environment; 41import android.os.Handler; 42import android.os.IBinder; 43import android.os.RemoteException; 44import android.os.ServiceManager; 45import android.os.UserHandle; 46import android.preference.PreferenceActivity; 47import android.preference.PreferenceFrameLayout; 48import android.provider.Settings; 49import android.support.v4.view.PagerAdapter; 50import android.support.v4.view.PagerTabStrip; 51import android.support.v4.view.ViewPager; 52import android.text.format.Formatter; 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.BaseAdapter; 65import android.widget.Filter; 66import android.widget.Filterable; 67import android.widget.ListView; 68import android.widget.TextView; 69 70import com.android.internal.app.IMediaContainerService; 71import com.android.internal.content.PackageHelper; 72import com.android.settings.R; 73import com.android.settings.Settings.RunningServicesActivity; 74import com.android.settings.Settings.StorageUseActivity; 75import com.android.settings.applications.ApplicationsState.AppEntry; 76import com.android.settings.deviceinfo.StorageMeasurement; 77import com.android.settings.Utils; 78 79import java.util.ArrayList; 80import java.util.Comparator; 81import java.util.List; 82 83final class CanBeOnSdCardChecker { 84 final IPackageManager mPm; 85 int mInstallLocation; 86 87 CanBeOnSdCardChecker() { 88 mPm = IPackageManager.Stub.asInterface( 89 ServiceManager.getService("package")); 90 } 91 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 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 124interface AppClickListener { 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 */ 135public 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 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 232 public void setContainerService(IMediaContainerService containerService) { 233 mContainerService = containerService; 234 updateStorageUsage(); 235 } 236 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 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 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 311 public void pause() { 312 if (mApplications != null) { 313 mApplications.pause(); 314 } 315 if (mRunningProcessesView != null) { 316 mRunningProcessesView.doPause(); 317 } 318 } 319 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 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 416 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 417 mClickListener.onItemClick(this, parent, view, position, id); 418 } 419 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 466 public int getCount() { 467 return mTabs.size(); 468 } 469 470 @Override 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 479 public void destroyItem(ViewGroup container, int position, Object object) { 480 container.removeView((View)object); 481 } 482 483 @Override 484 public boolean isViewFromObject(View view, Object object) { 485 return view == object; 486 } 487 488 @Override 489 public CharSequence getPageTitle(int position) { 490 return mTabs.get(position).mLabel; 491 } 492 493 @Override 494 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 495 } 496 497 @Override 498 public void onPageSelected(int position) { 499 mCurPos = position; 500 } 501 502 @Override 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 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 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 575 public void pause() { 576 if (mResumed) { 577 mResumed = false; 578 mSession.pause(); 579 } 580 } 581 582 public void rebuild(int sort) { 583 if (sort == mLastSortMode) { 584 return; 585 } 586 mLastSortMode = sort; 587 rebuild(true); 588 } 589 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 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 678 public void onRunningStateChanged(boolean running) { 679 mTab.mOwner.getActivity().setProgressBarIndeterminateVisibility(running); 680 } 681 682 @Override 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 700 public void onPackageListChanged() { 701 rebuild(false); 702 } 703 704 @Override 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 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 733 public void onAllSizesComputed() { 734 if (mLastSortMode == SORT_ORDER_SIZE) { 735 rebuild(false); 736 } 737 mTab.updateStorageUsage(); 738 } 739 740 public int getCount() { 741 return mEntries != null ? mEntries.size() : 0; 742 } 743 744 public Object getItem(int position) { 745 return mEntries.get(position); 746 } 747 748 public ApplicationsState.AppEntry getAppEntry(int position) { 749 return mEntries.get(position); 750 } 751 752 public long getItemId(int position) { 753 return mEntries.get(position).id; 754 } 755 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 holder.appName.setTextColor(mContext.getResources().getColorStateList( 769 entry.info.enabled ? android.R.color.primary_text_dark 770 : android.R.color.secondary_text_dark)); 771 } 772 mState.ensureIcon(entry); 773 if (entry.icon != null) { 774 holder.appIcon.setImageDrawable(entry.icon); 775 } 776 holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize); 777 if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { 778 holder.disabled.setVisibility(View.VISIBLE); 779 holder.disabled.setText(R.string.not_installed); 780 } else if (!entry.info.enabled) { 781 holder.disabled.setVisibility(View.VISIBLE); 782 holder.disabled.setText(R.string.disabled); 783 } else { 784 holder.disabled.setVisibility(View.GONE); 785 } 786 if (mFilterMode == FILTER_APPS_SDCARD) { 787 holder.checkBox.setVisibility(View.VISIBLE); 788 holder.checkBox.setChecked((entry.info.flags 789 & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); 790 } else { 791 holder.checkBox.setVisibility(View.GONE); 792 } 793 } 794 mActive.remove(convertView); 795 mActive.add(convertView); 796 return convertView; 797 } 798 799 @Override 800 public Filter getFilter() { 801 return mFilter; 802 } 803 804 @Override 805 public void onMovedToScrapHeap(View view) { 806 mActive.remove(view); 807 } 808 } 809 810 @Override 811 public void onCreate(Bundle savedInstanceState) { 812 super.onCreate(savedInstanceState); 813 814 setHasOptionsMenu(true); 815 816 mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); 817 Intent intent = getActivity().getIntent(); 818 String action = intent.getAction(); 819 int defaultListType = LIST_TYPE_DOWNLOADED; 820 String className = getArguments() != null 821 ? getArguments().getString("classname") : null; 822 if (className == null) { 823 className = intent.getComponent().getClassName(); 824 } 825 if (className.equals(RunningServicesActivity.class.getName()) 826 || className.endsWith(".RunningServices")) { 827 defaultListType = LIST_TYPE_RUNNING; 828 } else if (className.equals(StorageUseActivity.class.getName()) 829 || Intent.ACTION_MANAGE_PACKAGE_STORAGE.equals(action) 830 || className.endsWith(".StorageUse")) { 831 mSortOrder = SORT_ORDER_SIZE; 832 defaultListType = LIST_TYPE_ALL; 833 } else if (Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) { 834 // Select the all-apps list, with the default sorting 835 defaultListType = LIST_TYPE_ALL; 836 } 837 838 if (savedInstanceState != null) { 839 mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder); 840 int tmp = savedInstanceState.getInt(EXTRA_DEFAULT_LIST_TYPE, -1); 841 if (tmp != -1) defaultListType = tmp; 842 mShowBackground = savedInstanceState.getBoolean(EXTRA_SHOW_BACKGROUND, false); 843 } 844 845 mDefaultListType = defaultListType; 846 847 final Intent containerIntent = new Intent().setComponent( 848 StorageMeasurement.DEFAULT_CONTAINER_COMPONENT); 849 getActivity().bindService(containerIntent, mContainerConnection, Context.BIND_AUTO_CREATE); 850 851 mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value); 852 mComputingSizeStr = getActivity().getText(R.string.computing_size); 853 854 TabInfo tab = new TabInfo(this, mApplicationsState, 855 getActivity().getString(R.string.filter_apps_third_party), 856 LIST_TYPE_DOWNLOADED, this, savedInstanceState); 857 mTabs.add(tab); 858 859 if (!Environment.isExternalStorageEmulated()) { 860 tab = new TabInfo(this, mApplicationsState, 861 getActivity().getString(R.string.filter_apps_onsdcard), 862 LIST_TYPE_SDCARD, this, savedInstanceState); 863 mTabs.add(tab); 864 } 865 866 tab = new TabInfo(this, mApplicationsState, 867 getActivity().getString(R.string.filter_apps_running), 868 LIST_TYPE_RUNNING, this, savedInstanceState); 869 mTabs.add(tab); 870 871 tab = new TabInfo(this, mApplicationsState, 872 getActivity().getString(R.string.filter_apps_all), 873 LIST_TYPE_ALL, this, savedInstanceState); 874 mTabs.add(tab); 875 } 876 877 878 @Override 879 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 880 // initialize the inflater 881 mInflater = inflater; 882 883 View rootView = mInflater.inflate(R.layout.manage_applications_content, 884 container, false); 885 mContentContainer = container; 886 mRootView = rootView; 887 888 mViewPager = (ViewPager) rootView.findViewById(R.id.pager); 889 MyPagerAdapter adapter = new MyPagerAdapter(); 890 mViewPager.setAdapter(adapter); 891 mViewPager.setOnPageChangeListener(adapter); 892 PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs); 893 tabs.setTabIndicatorColorResource(android.R.color.holo_blue_light); 894 895 // We have to do this now because PreferenceFrameLayout looks at it 896 // only when the view is added. 897 if (container instanceof PreferenceFrameLayout) { 898 ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true; 899 } 900 901 if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_RESET_DIALOG)) { 902 buildResetDialog(); 903 } 904 905 if (savedInstanceState == null) { 906 // First time init: make sure view pager is showing the correct tab. 907 for (int i = 0; i < mTabs.size(); i++) { 908 TabInfo tab = mTabs.get(i); 909 if (tab.mListType == mDefaultListType) { 910 mViewPager.setCurrentItem(i); 911 break; 912 } 913 } 914 } 915 916 return rootView; 917 } 918 919 @Override 920 public void onStart() { 921 super.onStart(); 922 } 923 924 @Override 925 public void onResume() { 926 super.onResume(); 927 mActivityResumed = true; 928 updateCurrentTab(mViewPager.getCurrentItem()); 929 updateOptionsMenu(); 930 } 931 932 @Override 933 public void onSaveInstanceState(Bundle outState) { 934 super.onSaveInstanceState(outState); 935 outState.putInt(EXTRA_SORT_ORDER, mSortOrder); 936 if (mDefaultListType != -1) { 937 outState.putInt(EXTRA_DEFAULT_LIST_TYPE, mDefaultListType); 938 } 939 outState.putBoolean(EXTRA_SHOW_BACKGROUND, mShowBackground); 940 if (mResetDialog != null) { 941 outState.putBoolean(EXTRA_RESET_DIALOG, true); 942 } 943 } 944 945 @Override 946 public void onPause() { 947 super.onPause(); 948 mActivityResumed = false; 949 for (int i=0; i<mTabs.size(); i++) { 950 mTabs.get(i).pause(); 951 } 952 } 953 954 @Override 955 public void onStop() { 956 super.onStop(); 957 if (mResetDialog != null) { 958 mResetDialog.dismiss(); 959 mResetDialog = null; 960 } 961 } 962 963 @Override 964 public void onDestroyView() { 965 super.onDestroyView(); 966 967 // We are going to keep the tab data structures around, but they 968 // are no longer attached to their view hierarchy. 969 for (int i=0; i<mTabs.size(); i++) { 970 mTabs.get(i).detachView(); 971 } 972 } 973 974 @Override 975 public void onActivityResult(int requestCode, int resultCode, Intent data) { 976 if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { 977 mApplicationsState.requestSize(mCurrentPkgName); 978 } 979 } 980 981 TabInfo tabForType(int type) { 982 for (int i = 0; i < mTabs.size(); i++) { 983 TabInfo tab = mTabs.get(i); 984 if (tab.mListType == type) { 985 return tab; 986 } 987 } 988 return null; 989 } 990 991 // utility method used to start sub activity 992 private void startApplicationDetailsActivity() { 993 // start new fragment to display extended information 994 Bundle args = new Bundle(); 995 args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mCurrentPkgName); 996 997 PreferenceActivity pa = (PreferenceActivity)getActivity(); 998 pa.startPreferencePanel(InstalledAppDetails.class.getName(), args, 999 R.string.application_info_label, null, this, INSTALLED_APP_DETAILS); 1000 } 1001 1002 @Override 1003 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 1004 mOptionsMenu = menu; 1005 // note: icons removed for now because the cause the new action 1006 // bar UI to be very confusing. 1007 menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha) 1008 //.setIcon(android.R.drawable.ic_menu_sort_alphabetically) 1009 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 1010 menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size) 1011 //.setIcon(android.R.drawable.ic_menu_sort_by_size) 1012 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 1013 menu.add(0, SHOW_RUNNING_SERVICES, 3, R.string.show_running_services) 1014 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 1015 menu.add(0, SHOW_BACKGROUND_PROCESSES, 3, R.string.show_background_processes) 1016 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 1017 menu.add(0, RESET_APP_PREFERENCES, 4, R.string.reset_app_preferences) 1018 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 1019 updateOptionsMenu(); 1020 } 1021 1022 @Override 1023 public void onPrepareOptionsMenu(Menu menu) { 1024 updateOptionsMenu(); 1025 } 1026 1027 @Override 1028 public void onDestroyOptionsMenu() { 1029 mOptionsMenu = null; 1030 } 1031 1032 @Override 1033 public void onDestroy() { 1034 getActivity().unbindService(mContainerConnection); 1035 super.onDestroy(); 1036 } 1037 1038 void updateOptionsMenu() { 1039 if (mOptionsMenu == null) { 1040 return; 1041 } 1042 1043 /* 1044 * The running processes screen doesn't use the mApplicationsAdapter 1045 * so bringing up this menu in that case doesn't make any sense. 1046 */ 1047 if (mCurTab != null && mCurTab.mListType == LIST_TYPE_RUNNING) { 1048 TabInfo tab = tabForType(LIST_TYPE_RUNNING); 1049 boolean showingBackground = tab != null && tab.mRunningProcessesView != null 1050 ? tab.mRunningProcessesView.mAdapter.getShowBackground() : false; 1051 mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(false); 1052 mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(false); 1053 mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(showingBackground); 1054 mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(!showingBackground); 1055 mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(false); 1056 } else { 1057 mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA); 1058 mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE); 1059 mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(false); 1060 mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(false); 1061 mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(true); 1062 } 1063 } 1064 1065 void buildResetDialog() { 1066 if (mResetDialog == null) { 1067 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 1068 builder.setTitle(R.string.reset_app_preferences_title); 1069 builder.setMessage(R.string.reset_app_preferences_desc); 1070 builder.setPositiveButton(R.string.reset_app_preferences_button, this); 1071 builder.setNegativeButton(R.string.cancel, null); 1072 mResetDialog = builder.show(); 1073 mResetDialog.setOnDismissListener(this); 1074 } 1075 } 1076 1077 @Override 1078 public void onDismiss(DialogInterface dialog) { 1079 if (mResetDialog == dialog) { 1080 mResetDialog = null; 1081 } 1082 } 1083 1084 1085 @Override 1086 public void onClick(DialogInterface dialog, int which) { 1087 if (mResetDialog == dialog) { 1088 final PackageManager pm = getActivity().getPackageManager(); 1089 final INotificationManager nm = INotificationManager.Stub.asInterface( 1090 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 1091 final NetworkPolicyManager npm = NetworkPolicyManager.from(getActivity()); 1092 final Handler handler = new Handler(getActivity().getMainLooper()); 1093 (new AsyncTask<Void, Void, Void>() { 1094 @Override protected Void doInBackground(Void... params) { 1095 List<ApplicationInfo> apps = pm.getInstalledApplications( 1096 PackageManager.GET_DISABLED_COMPONENTS); 1097 for (int i=0; i<apps.size(); i++) { 1098 ApplicationInfo app = apps.get(i); 1099 try { 1100 if (DEBUG) Log.v(TAG, "Enabling notifications: " + app.packageName); 1101 nm.setNotificationsEnabledForPackage(app.packageName, true); 1102 } catch (android.os.RemoteException ex) { 1103 } 1104 if (DEBUG) Log.v(TAG, "Clearing preferred: " + app.packageName); 1105 pm.clearPackagePreferredActivities(app.packageName); 1106 if (!app.enabled) { 1107 if (DEBUG) Log.v(TAG, "Enabling app: " + app.packageName); 1108 if (pm.getApplicationEnabledSetting(app.packageName) 1109 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { 1110 pm.setApplicationEnabledSetting(app.packageName, 1111 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 1112 PackageManager.DONT_KILL_APP); 1113 } 1114 } 1115 } 1116 // We should have cleared all of the preferred apps above; 1117 // just in case some may be lingering, retrieve whatever is 1118 // still set and remove it. 1119 ArrayList<IntentFilter> filters = new ArrayList<IntentFilter>(); 1120 ArrayList<ComponentName> prefActivities = new ArrayList<ComponentName>(); 1121 pm.getPreferredActivities(filters, prefActivities, null); 1122 for (int i=0; i<prefActivities.size(); i++) { 1123 if (DEBUG) Log.v(TAG, "Clearing preferred: " 1124 + prefActivities.get(i).getPackageName()); 1125 pm.clearPackagePreferredActivities(prefActivities.get(i).getPackageName()); 1126 } 1127 final int[] restrictedUids = npm.getUidsWithPolicy( 1128 POLICY_REJECT_METERED_BACKGROUND); 1129 final int currentUserId = ActivityManager.getCurrentUser(); 1130 for (int uid : restrictedUids) { 1131 // Only reset for current user 1132 if (UserHandle.getUserId(uid) == currentUserId) { 1133 if (DEBUG) Log.v(TAG, "Clearing data policy: " + uid); 1134 npm.setUidPolicy(uid, POLICY_NONE); 1135 } 1136 } 1137 handler.post(new Runnable() { 1138 @Override public void run() { 1139 if (DEBUG) Log.v(TAG, "Done clearing"); 1140 if (getActivity() != null && mActivityResumed) { 1141 if (DEBUG) Log.v(TAG, "Updating UI!"); 1142 for (int i=0; i<mTabs.size(); i++) { 1143 TabInfo tab = mTabs.get(i); 1144 if (tab.mApplications != null) { 1145 tab.mApplications.pause(); 1146 } 1147 } 1148 if (mCurTab != null) { 1149 mCurTab.resume(mSortOrder); 1150 } 1151 } 1152 } 1153 }); 1154 return null; 1155 } 1156 }).execute(); 1157 } 1158 } 1159 1160 @Override 1161 public boolean onOptionsItemSelected(MenuItem item) { 1162 int menuId = item.getItemId(); 1163 if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) { 1164 mSortOrder = menuId; 1165 if (mCurTab != null && mCurTab.mApplications != null) { 1166 mCurTab.mApplications.rebuild(mSortOrder); 1167 } 1168 } else if (menuId == SHOW_RUNNING_SERVICES) { 1169 mShowBackground = false; 1170 if (mCurTab != null && mCurTab.mRunningProcessesView != null) { 1171 mCurTab.mRunningProcessesView.mAdapter.setShowBackground(false); 1172 } 1173 } else if (menuId == SHOW_BACKGROUND_PROCESSES) { 1174 mShowBackground = true; 1175 if (mCurTab != null && mCurTab.mRunningProcessesView != null) { 1176 mCurTab.mRunningProcessesView.mAdapter.setShowBackground(true); 1177 } 1178 } else if (menuId == RESET_APP_PREFERENCES) { 1179 buildResetDialog(); 1180 } else { 1181 // Handle the home button 1182 return false; 1183 } 1184 updateOptionsMenu(); 1185 return true; 1186 } 1187 1188 public void onItemClick(TabInfo tab, AdapterView<?> parent, View view, int position, 1189 long id) { 1190 if (tab.mApplications != null && tab.mApplications.getCount() > position) { 1191 ApplicationsState.AppEntry entry = tab.mApplications.getAppEntry(position); 1192 mCurrentPkgName = entry.info.packageName; 1193 startApplicationDetailsActivity(); 1194 } 1195 } 1196 1197 public void updateCurrentTab(int position) { 1198 TabInfo tab = mTabs.get(position); 1199 mCurTab = tab; 1200 1201 // Put things in the correct paused/resumed state. 1202 if (mActivityResumed) { 1203 mCurTab.build(mInflater, mContentContainer, mRootView); 1204 mCurTab.resume(mSortOrder); 1205 } else { 1206 mCurTab.pause(); 1207 } 1208 for (int i=0; i<mTabs.size(); i++) { 1209 TabInfo t = mTabs.get(i); 1210 if (t != mCurTab) { 1211 t.pause(); 1212 } 1213 } 1214 1215 mCurTab.updateStorageUsage(); 1216 updateOptionsMenu(); 1217 final Activity host = getActivity(); 1218 if (host != null) { 1219 host.invalidateOptionsMenu(); 1220 } 1221 } 1222 1223 private volatile IMediaContainerService mContainerService; 1224 1225 private final ServiceConnection mContainerConnection = new ServiceConnection() { 1226 @Override 1227 public void onServiceConnected(ComponentName name, IBinder service) { 1228 mContainerService = IMediaContainerService.Stub.asInterface(service); 1229 for (int i=0; i<mTabs.size(); i++) { 1230 mTabs.get(i).setContainerService(mContainerService); 1231 } 1232 } 1233 1234 @Override 1235 public void onServiceDisconnected(ComponentName name) { 1236 mContainerService = null; 1237 } 1238 }; 1239} 1240