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