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