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