PrivateVolumeSettings.java revision 4bbe3d1fb66a99bfdf4cad8f2e77df23dccebbbd
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings.deviceinfo; 18 19import static com.android.settings.deviceinfo.StorageSettings.TAG; 20 21import android.app.AlertDialog; 22import android.app.Dialog; 23import android.app.DialogFragment; 24import android.app.Fragment; 25import android.content.ActivityNotFoundException; 26import android.content.Context; 27import android.content.DialogInterface; 28import android.content.Intent; 29import android.content.pm.IPackageDataObserver; 30import android.content.pm.PackageInfo; 31import android.content.pm.PackageManager; 32import android.content.pm.UserInfo; 33import android.os.Bundle; 34import android.os.Environment; 35import android.os.UserHandle; 36import android.os.UserManager; 37import android.os.storage.StorageEventListener; 38import android.os.storage.StorageManager; 39import android.os.storage.VolumeInfo; 40import android.os.storage.VolumeRecord; 41import android.preference.Preference; 42import android.preference.PreferenceCategory; 43import android.preference.PreferenceGroup; 44import android.preference.PreferenceScreen; 45import android.provider.DocumentsContract; 46import android.text.TextUtils; 47import android.text.format.Formatter; 48import android.text.format.Formatter.BytesResult; 49import android.util.Log; 50import android.view.LayoutInflater; 51import android.view.Menu; 52import android.view.MenuInflater; 53import android.view.MenuItem; 54import android.view.View; 55import android.widget.EditText; 56 57import com.android.internal.logging.MetricsLogger; 58import com.android.settings.R; 59import com.android.settings.Settings.StorageUseActivity; 60import com.android.settings.SettingsPreferenceFragment; 61import com.android.settings.Utils; 62import com.android.settings.applications.ManageApplications; 63import com.android.settings.deviceinfo.StorageSettings.MountTask; 64import com.android.settingslib.deviceinfo.StorageMeasurement; 65import com.android.settingslib.deviceinfo.StorageMeasurement.MeasurementDetails; 66import com.android.settingslib.deviceinfo.StorageMeasurement.MeasurementReceiver; 67import com.google.android.collect.Lists; 68 69import java.io.File; 70import java.util.HashMap; 71import java.util.List; 72import java.util.Objects; 73 74/** 75 * Panel showing summary and actions for a {@link VolumeInfo#TYPE_PRIVATE} 76 * storage volume. 77 */ 78public class PrivateVolumeSettings extends SettingsPreferenceFragment { 79 // TODO: disable unmount when providing over MTP/PTP 80 // TODO: warn when mounted read-only 81 82 private static final String TAG_RENAME = "rename"; 83 private static final String TAG_OTHER_INFO = "otherInfo"; 84 private static final String TAG_USER_INFO = "userInfo"; 85 private static final String TAG_CONFIRM_CLEAR_CACHE = "confirmClearCache"; 86 87 private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents"; 88 89 private static final int[] ITEMS_NO_SHOW_SHARED = new int[] { 90 R.string.storage_detail_apps, 91 }; 92 93 private static final int[] ITEMS_SHOW_SHARED = new int[] { 94 R.string.storage_detail_apps, 95 R.string.storage_detail_images, 96 R.string.storage_detail_videos, 97 R.string.storage_detail_audio, 98 R.string.storage_detail_other 99 }; 100 101 private StorageManager mStorageManager; 102 private UserManager mUserManager; 103 104 private String mVolumeId; 105 private VolumeInfo mVolume; 106 private VolumeInfo mSharedVolume; 107 108 private StorageMeasurement mMeasure; 109 110 private UserInfo mCurrentUser; 111 112 private StorageSummaryPreference mSummary; 113 private List<StorageItemPreference> mItemPreferencePool = Lists.newArrayList(); 114 private List<PreferenceCategory> mHeaderPreferencePool = Lists.newArrayList(); 115 private int mHeaderPoolIndex; 116 private int mItemPoolIndex; 117 118 private Preference mExplore; 119 120 private boolean isVolumeValid() { 121 return (mVolume != null) && (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) 122 && mVolume.isMountedReadable(); 123 } 124 125 @Override 126 protected int getMetricsCategory() { 127 return MetricsLogger.DEVICEINFO_STORAGE; 128 } 129 130 @Override 131 public void onCreate(Bundle icicle) { 132 super.onCreate(icicle); 133 134 final Context context = getActivity(); 135 136 mUserManager = context.getSystemService(UserManager.class); 137 mStorageManager = context.getSystemService(StorageManager.class); 138 139 mVolumeId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID); 140 mVolume = mStorageManager.findVolumeById(mVolumeId); 141 142 // Find the emulated shared storage layered above this private volume 143 mSharedVolume = mStorageManager.findEmulatedForPrivate(mVolume); 144 145 mMeasure = new StorageMeasurement(context, mVolume, mSharedVolume); 146 mMeasure.setReceiver(mReceiver); 147 148 if (!isVolumeValid()) { 149 getActivity().finish(); 150 return; 151 } 152 153 addPreferencesFromResource(R.xml.device_info_storage_volume); 154 getPreferenceScreen().setOrderingAsAdded(true); 155 156 mSummary = new StorageSummaryPreference(context); 157 mCurrentUser = mUserManager.getUserInfo(UserHandle.myUserId()); 158 159 mExplore = buildAction(R.string.storage_menu_explore); 160 161 setHasOptionsMenu(true); 162 } 163 164 public void update() { 165 if (!isVolumeValid()) { 166 getActivity().finish(); 167 return; 168 } 169 170 getActivity().setTitle(mStorageManager.getBestVolumeDescription(mVolume)); 171 172 // Valid options may have changed 173 getFragmentManager().invalidateOptionsMenu(); 174 175 final Context context = getActivity(); 176 final PreferenceScreen screen = getPreferenceScreen(); 177 178 screen.removeAll(); 179 180 addPreference(screen, mSummary); 181 182 List<UserInfo> allUsers = mUserManager.getUsers(); 183 final int userCount = allUsers.size(); 184 final boolean showHeaders = userCount > 1; 185 final boolean showShared = (mSharedVolume != null) && mSharedVolume.isMountedReadable(); 186 187 mItemPoolIndex = 0; 188 mHeaderPoolIndex = 0; 189 190 int addedUserCount = 0; 191 // Add current user and its profiles first 192 for (int userIndex = 0; userIndex < userCount; ++userIndex) { 193 final UserInfo userInfo = allUsers.get(userIndex); 194 if (isProfileOf(mCurrentUser, userInfo)) { 195 PreferenceCategory details = addCategory(screen, 196 showHeaders ? userInfo.name : null); 197 addDetailItems(details, showShared, userInfo.id); 198 ++addedUserCount; 199 } 200 } 201 202 // Add rest of users 203 if (userCount - addedUserCount > 0) { 204 PreferenceCategory otherUsers = addCategory(screen, 205 getText(R.string.storage_other_users)); 206 for (int userIndex = 0; userIndex < userCount; ++userIndex) { 207 final UserInfo userInfo = allUsers.get(userIndex); 208 if (!isProfileOf(mCurrentUser, userInfo)) { 209 addItem(otherUsers, /* titleRes */ 0, userInfo.name, userInfo.id); 210 } 211 } 212 } 213 214 addItem(screen, R.string.storage_detail_cached, null, UserHandle.USER_NULL); 215 216 if (showShared) { 217 addPreference(screen, mExplore); 218 } 219 220 final File file = mVolume.getPath(); 221 final long totalBytes = file.getTotalSpace(); 222 final long freeBytes = file.getFreeSpace(); 223 final long usedBytes = totalBytes - freeBytes; 224 225 final BytesResult result = Formatter.formatBytes(getResources(), usedBytes, 0); 226 mSummary.setTitle(TextUtils.expandTemplate(getText(R.string.storage_size_large), 227 result.value, result.units)); 228 mSummary.setSummary(getString(R.string.storage_volume_used, 229 Formatter.formatFileSize(context, totalBytes))); 230 mSummary.setPercent((int) ((usedBytes * 100) / totalBytes)); 231 232 mMeasure.forceMeasure(); 233 } 234 235 private void addPreference(PreferenceGroup group, Preference pref) { 236 pref.setOrder(Preference.DEFAULT_ORDER); 237 group.addPreference(pref); 238 } 239 240 private PreferenceCategory addCategory(PreferenceGroup group, CharSequence title) { 241 PreferenceCategory category; 242 if (mHeaderPoolIndex < mHeaderPreferencePool.size()) { 243 category = mHeaderPreferencePool.get(mHeaderPoolIndex); 244 } else { 245 category = new PreferenceCategory(getActivity(), null, 246 com.android.internal.R.attr.preferenceCategoryStyle); 247 mHeaderPreferencePool.add(category); 248 } 249 category.setTitle(title); 250 category.removeAll(); 251 addPreference(group, category); 252 ++mHeaderPoolIndex; 253 return category; 254 } 255 256 private void addDetailItems(PreferenceCategory category, boolean showShared, int userId) { 257 final int[] itemsToAdd = (showShared ? ITEMS_SHOW_SHARED : ITEMS_NO_SHOW_SHARED); 258 for (int i = 0; i < itemsToAdd.length; ++i) { 259 addItem(category, itemsToAdd[i], null, userId); 260 } 261 } 262 263 private void addItem(PreferenceGroup group, int titleRes, CharSequence title, int userId) { 264 StorageItemPreference item; 265 if (mItemPoolIndex < mItemPreferencePool.size()) { 266 item = mItemPreferencePool.get(mItemPoolIndex); 267 } else { 268 item = buildItem(); 269 mItemPreferencePool.add(item); 270 } 271 if (title != null) { 272 item.setTitle(title); 273 } else { 274 item.setTitle(titleRes); 275 } 276 item.setSummary(R.string.memory_calculating_size); 277 item.userHandle = userId; 278 addPreference(group, item); 279 ++mItemPoolIndex; 280 } 281 282 private StorageItemPreference buildItem() { 283 final StorageItemPreference item = new StorageItemPreference(getActivity()); 284 return item; 285 } 286 287 private Preference buildAction(int titleRes) { 288 final Preference pref = new Preference(getActivity()); 289 pref.setTitle(titleRes); 290 return pref; 291 } 292 293 @Override 294 public void onResume() { 295 super.onResume(); 296 297 // Refresh to verify that we haven't been formatted away 298 mVolume = mStorageManager.findVolumeById(mVolumeId); 299 if (!isVolumeValid()) { 300 getActivity().finish(); 301 return; 302 } 303 304 mStorageManager.registerListener(mStorageListener); 305 update(); 306 } 307 308 @Override 309 public void onPause() { 310 super.onPause(); 311 mStorageManager.unregisterListener(mStorageListener); 312 } 313 314 @Override 315 public void onDestroy() { 316 super.onDestroy(); 317 if (mMeasure != null) { 318 mMeasure.onDestroy(); 319 } 320 } 321 322 @Override 323 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 324 inflater.inflate(R.menu.storage_volume, menu); 325 } 326 327 @Override 328 public void onPrepareOptionsMenu(Menu menu) { 329 if (!isVolumeValid()) return; 330 331 final MenuItem rename = menu.findItem(R.id.storage_rename); 332 final MenuItem mount = menu.findItem(R.id.storage_mount); 333 final MenuItem unmount = menu.findItem(R.id.storage_unmount); 334 final MenuItem format = menu.findItem(R.id.storage_format); 335 final MenuItem migrate = menu.findItem(R.id.storage_migrate); 336 337 // Actions live in menu for non-internal private volumes; they're shown 338 // as preference items for public volumes. 339 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(mVolume.getId())) { 340 rename.setVisible(false); 341 mount.setVisible(false); 342 unmount.setVisible(false); 343 format.setVisible(false); 344 } else { 345 rename.setVisible(mVolume.getType() == VolumeInfo.TYPE_PRIVATE); 346 mount.setVisible(mVolume.getState() == VolumeInfo.STATE_UNMOUNTED); 347 unmount.setVisible(mVolume.isMountedReadable()); 348 format.setVisible(true); 349 } 350 351 format.setTitle(R.string.storage_menu_format_public); 352 353 // Only offer to migrate when not current storage 354 final VolumeInfo privateVol = getActivity().getPackageManager() 355 .getPrimaryStorageCurrentVolume(); 356 migrate.setVisible(!Objects.equals(mVolume, privateVol)); 357 } 358 359 @Override 360 public boolean onOptionsItemSelected(MenuItem item) { 361 final Context context = getActivity(); 362 final Bundle args = new Bundle(); 363 switch (item.getItemId()) { 364 case R.id.storage_rename: 365 RenameFragment.show(this, mVolume); 366 return true; 367 case R.id.storage_mount: 368 new MountTask(context, mVolume).execute(); 369 return true; 370 case R.id.storage_unmount: 371 args.putString(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId()); 372 startFragment(this, PrivateVolumeUnmount.class.getCanonicalName(), 373 R.string.storage_menu_unmount, 0, args); 374 return true; 375 case R.id.storage_format: 376 args.putString(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId()); 377 startFragment(this, PrivateVolumeFormat.class.getCanonicalName(), 378 R.string.storage_menu_format, 0, args); 379 return true; 380 case R.id.storage_migrate: 381 final Intent intent = new Intent(context, StorageWizardMigrateConfirm.class); 382 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId()); 383 startActivity(intent); 384 return true; 385 } 386 return super.onOptionsItemSelected(item); 387 } 388 389 @Override 390 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference pref) { 391 // TODO: launch better intents for specific volume 392 393 final int userId = (pref instanceof StorageItemPreference ? 394 ((StorageItemPreference)pref).userHandle : -1); 395 final int itemTitleId = pref.getTitleRes(); 396 Intent intent = null; 397 switch (itemTitleId) { 398 case R.string.storage_detail_apps: { 399 Bundle args = new Bundle(); 400 args.putString(ManageApplications.EXTRA_CLASSNAME, 401 StorageUseActivity.class.getName()); 402 args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid()); 403 args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription()); 404 intent = Utils.onBuildStartFragmentIntent(getActivity(), 405 ManageApplications.class.getName(), args, null, R.string.apps_storage, null, 406 false); 407 408 } break; 409 case R.string.storage_detail_images: { 410 intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT); 411 intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "images_root")); 412 intent.addCategory(Intent.CATEGORY_DEFAULT); 413 414 } break; 415 case R.string.storage_detail_videos: { 416 intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT); 417 intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "videos_root")); 418 intent.addCategory(Intent.CATEGORY_DEFAULT); 419 420 } break; 421 case R.string.storage_detail_audio: { 422 intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT); 423 intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "audio_root")); 424 intent.addCategory(Intent.CATEGORY_DEFAULT); 425 426 } break; 427 case R.string.storage_detail_other: { 428 OtherInfoFragment.show(this, mStorageManager.getBestVolumeDescription(mVolume), 429 mSharedVolume); 430 return true; 431 432 } 433 case R.string.storage_detail_cached: { 434 ConfirmClearCacheFragment.show(this); 435 return true; 436 437 } 438 case R.string.storage_menu_explore: { 439 intent = mSharedVolume.buildBrowseIntent(); 440 } break; 441 case 0: { 442 UserInfoFragment.show(this, pref.getTitle(), pref.getSummary()); 443 return true; 444 } 445 } 446 447 if (intent != null) { 448 try { 449 if (userId == -1) { 450 startActivity(intent); 451 } else { 452 getActivity().startActivityAsUser(intent, new UserHandle(userId)); 453 } 454 } catch (ActivityNotFoundException e) { 455 Log.w(TAG, "No activity found for " + intent); 456 } 457 return true; 458 } 459 return super.onPreferenceTreeClick(preferenceScreen, pref); 460 } 461 462 private final MeasurementReceiver mReceiver = new MeasurementReceiver() { 463 @Override 464 public void onDetailsChanged(MeasurementDetails details) { 465 updateDetails(details); 466 } 467 }; 468 469 private void updateDetails(MeasurementDetails details) { 470 for (int i = 0; i < mItemPoolIndex; ++i) { 471 StorageItemPreference item = mItemPreferencePool.get(i); 472 final int userId = item.userHandle; 473 final int itemTitleId = item.getTitleRes(); 474 switch (itemTitleId) { 475 case R.string.storage_detail_apps: { 476 updatePreference(item, details.appsSize.get(userId)); 477 } break; 478 case R.string.storage_detail_images: { 479 final long imagesSize = totalValues(details, userId, 480 Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES, 481 Environment.DIRECTORY_PICTURES); 482 updatePreference(item, imagesSize); 483 } break; 484 case R.string.storage_detail_videos: { 485 final long videosSize = totalValues(details, userId, 486 Environment.DIRECTORY_MOVIES); 487 updatePreference(item, videosSize); 488 } break; 489 case R.string.storage_detail_audio: { 490 final long audioSize = totalValues(details, userId, 491 Environment.DIRECTORY_MUSIC, 492 Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS, 493 Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS); 494 updatePreference(item, audioSize); 495 } break; 496 case R.string.storage_detail_other: { 497 updatePreference(item, details.miscSize.get(userId)); 498 } break; 499 case R.string.storage_detail_cached: { 500 updatePreference(item, details.cacheSize); 501 } break; 502 case 0: { 503 final long userSize = details.usersSize.get(userId); 504 updatePreference(item, userSize); 505 } break; 506 } 507 } 508 } 509 510 private void updatePreference(StorageItemPreference pref, long size) { 511 pref.setSummary(Formatter.formatFileSize(getActivity(), size)); 512 } 513 514 private boolean isProfileOf(UserInfo user, UserInfo profile) { 515 return user.id == profile.id || 516 (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID 517 && user.profileGroupId == profile.profileGroupId); 518 } 519 520 private static long totalValues(MeasurementDetails details, int userId, String... keys) { 521 long total = 0; 522 HashMap<String, Long> map = details.mediaSize.get(userId); 523 if (map != null) { 524 for (String key : keys) { 525 if (map.containsKey(key)) { 526 total += map.get(key); 527 } 528 } 529 } else { 530 throw new IllegalStateException( 531 "MeasurementDetails mediaSize array does not have key for user " + userId); 532 } 533 return total; 534 } 535 536 private final StorageEventListener mStorageListener = new StorageEventListener() { 537 @Override 538 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 539 if (Objects.equals(mVolume.getId(), vol.getId())) { 540 mVolume = vol; 541 update(); 542 } 543 } 544 545 @Override 546 public void onVolumeRecordChanged(VolumeRecord rec) { 547 if (Objects.equals(mVolume.getFsUuid(), rec.getFsUuid())) { 548 mVolume = mStorageManager.findVolumeById(mVolumeId); 549 update(); 550 } 551 } 552 }; 553 554 /** 555 * Dialog that allows editing of volume nickname. 556 */ 557 public static class RenameFragment extends DialogFragment { 558 public static void show(PrivateVolumeSettings parent, VolumeInfo vol) { 559 if (!parent.isAdded()) return; 560 561 final RenameFragment dialog = new RenameFragment(); 562 dialog.setTargetFragment(parent, 0); 563 final Bundle args = new Bundle(); 564 args.putString(VolumeRecord.EXTRA_FS_UUID, vol.getFsUuid()); 565 dialog.setArguments(args); 566 dialog.show(parent.getFragmentManager(), TAG_RENAME); 567 } 568 569 @Override 570 public Dialog onCreateDialog(Bundle savedInstanceState) { 571 final Context context = getActivity(); 572 final StorageManager storageManager = context.getSystemService(StorageManager.class); 573 574 final String fsUuid = getArguments().getString(VolumeRecord.EXTRA_FS_UUID); 575 final VolumeInfo vol = storageManager.findVolumeByUuid(fsUuid); 576 final VolumeRecord rec = storageManager.findRecordByUuid(fsUuid); 577 578 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 579 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext()); 580 581 final View view = dialogInflater.inflate(R.layout.dialog_edittext, null, false); 582 final EditText nickname = (EditText) view.findViewById(R.id.edittext); 583 nickname.setText(rec.getNickname()); 584 585 builder.setTitle(R.string.storage_rename_title); 586 builder.setView(view); 587 588 builder.setPositiveButton(R.string.save, 589 new DialogInterface.OnClickListener() { 590 @Override 591 public void onClick(DialogInterface dialog, int which) { 592 // TODO: move to background thread 593 storageManager.setVolumeNickname(fsUuid, 594 nickname.getText().toString()); 595 } 596 }); 597 builder.setNegativeButton(R.string.cancel, null); 598 599 return builder.create(); 600 } 601 } 602 603 public static class OtherInfoFragment extends DialogFragment { 604 public static void show(Fragment parent, String title, VolumeInfo sharedVol) { 605 if (!parent.isAdded()) return; 606 607 final OtherInfoFragment dialog = new OtherInfoFragment(); 608 dialog.setTargetFragment(parent, 0); 609 final Bundle args = new Bundle(); 610 args.putString(Intent.EXTRA_TITLE, title); 611 args.putParcelable(Intent.EXTRA_INTENT, sharedVol.buildBrowseIntent()); 612 dialog.setArguments(args); 613 dialog.show(parent.getFragmentManager(), TAG_OTHER_INFO); 614 } 615 616 @Override 617 public Dialog onCreateDialog(Bundle savedInstanceState) { 618 final Context context = getActivity(); 619 620 final String title = getArguments().getString(Intent.EXTRA_TITLE); 621 final Intent intent = getArguments().getParcelable(Intent.EXTRA_INTENT); 622 623 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 624 builder.setMessage( 625 TextUtils.expandTemplate(getText(R.string.storage_detail_dialog_other), title)); 626 627 builder.setPositiveButton(R.string.storage_menu_explore, 628 new DialogInterface.OnClickListener() { 629 @Override 630 public void onClick(DialogInterface dialog, int which) { 631 startActivity(intent); 632 } 633 }); 634 builder.setNegativeButton(android.R.string.cancel, null); 635 636 return builder.create(); 637 } 638 } 639 640 public static class UserInfoFragment extends DialogFragment { 641 public static void show(Fragment parent, CharSequence userLabel, CharSequence userSize) { 642 if (!parent.isAdded()) return; 643 644 final UserInfoFragment dialog = new UserInfoFragment(); 645 dialog.setTargetFragment(parent, 0); 646 final Bundle args = new Bundle(); 647 args.putCharSequence(Intent.EXTRA_TITLE, userLabel); 648 args.putCharSequence(Intent.EXTRA_SUBJECT, userSize); 649 dialog.setArguments(args); 650 dialog.show(parent.getFragmentManager(), TAG_USER_INFO); 651 } 652 653 @Override 654 public Dialog onCreateDialog(Bundle savedInstanceState) { 655 final Context context = getActivity(); 656 657 final CharSequence userLabel = getArguments().getCharSequence(Intent.EXTRA_TITLE); 658 final CharSequence userSize = getArguments().getCharSequence(Intent.EXTRA_SUBJECT); 659 660 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 661 builder.setMessage(TextUtils.expandTemplate( 662 getText(R.string.storage_detail_dialog_user), userLabel, userSize)); 663 664 builder.setPositiveButton(android.R.string.ok, null); 665 666 return builder.create(); 667 } 668 } 669 670 /** 671 * Dialog to request user confirmation before clearing all cache data. 672 */ 673 public static class ConfirmClearCacheFragment extends DialogFragment { 674 public static void show(Fragment parent) { 675 if (!parent.isAdded()) return; 676 677 final ConfirmClearCacheFragment dialog = new ConfirmClearCacheFragment(); 678 dialog.setTargetFragment(parent, 0); 679 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_CLEAR_CACHE); 680 } 681 682 @Override 683 public Dialog onCreateDialog(Bundle savedInstanceState) { 684 final Context context = getActivity(); 685 686 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 687 builder.setTitle(R.string.memory_clear_cache_title); 688 builder.setMessage(getString(R.string.memory_clear_cache_message)); 689 690 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 691 @Override 692 public void onClick(DialogInterface dialog, int which) { 693 final PrivateVolumeSettings target = (PrivateVolumeSettings) getTargetFragment(); 694 final PackageManager pm = context.getPackageManager(); 695 final List<PackageInfo> infos = pm.getInstalledPackages(0); 696 final ClearCacheObserver observer = new ClearCacheObserver( 697 target, infos.size()); 698 for (PackageInfo info : infos) { 699 pm.deleteApplicationCacheFiles(info.packageName, observer); 700 } 701 } 702 }); 703 builder.setNegativeButton(android.R.string.cancel, null); 704 705 return builder.create(); 706 } 707 } 708 709 private static class ClearCacheObserver extends IPackageDataObserver.Stub { 710 private final PrivateVolumeSettings mTarget; 711 private int mRemaining; 712 713 public ClearCacheObserver(PrivateVolumeSettings target, int remaining) { 714 mTarget = target; 715 mRemaining = remaining; 716 } 717 718 @Override 719 public void onRemoveCompleted(final String packageName, final boolean succeeded) { 720 synchronized (this) { 721 if (--mRemaining == 0) { 722 mTarget.update(); 723 } 724 } 725 } 726 } 727} 728