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 17 package com.android.settings.deviceinfo; 18 19 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 20 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.app.Fragment; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.Intent; 27 import android.graphics.Color; 28 import android.graphics.drawable.Drawable; 29 import android.os.AsyncTask; 30 import android.os.Bundle; 31 import android.os.UserHandle; 32 import android.os.UserManager; 33 import android.os.storage.DiskInfo; 34 import android.os.storage.StorageEventListener; 35 import android.os.storage.StorageManager; 36 import android.os.storage.VolumeInfo; 37 import android.os.storage.VolumeRecord; 38 import android.support.annotation.NonNull; 39 import android.support.annotation.VisibleForTesting; 40 import android.support.v7.preference.Preference; 41 import android.support.v7.preference.PreferenceCategory; 42 import android.text.TextUtils; 43 import android.text.format.Formatter; 44 import android.text.format.Formatter.BytesResult; 45 import android.util.Log; 46 import android.widget.Toast; 47 48 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 49 import com.android.settings.R; 50 import com.android.settings.SettingsPreferenceFragment; 51 import com.android.settings.core.SubSettingLauncher; 52 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 53 import com.android.settings.dashboard.SummaryLoader; 54 import com.android.settings.search.BaseSearchIndexProvider; 55 import com.android.settings.search.Indexable; 56 import com.android.settings.search.SearchIndexableRaw; 57 import com.android.settingslib.RestrictedLockUtils; 58 import com.android.settingslib.deviceinfo.PrivateStorageInfo; 59 import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider; 60 61 import java.text.NumberFormat; 62 import java.util.ArrayList; 63 import java.util.Collections; 64 import java.util.List; 65 66 /** 67 * Panel showing both internal storage (both built-in storage and private 68 * volumes) and removable storage (public volumes). 69 */ 70 public class StorageSettings extends SettingsPreferenceFragment implements Indexable { 71 static final String TAG = "StorageSettings"; 72 73 private static final String TAG_VOLUME_UNMOUNTED = "volume_unmounted"; 74 private static final String TAG_DISK_INIT = "disk_init"; 75 private static final int METRICS_CATEGORY = MetricsEvent.DEVICEINFO_STORAGE; 76 77 static final int COLOR_PUBLIC = Color.parseColor("#ff9e9e9e"); 78 79 static final int[] COLOR_PRIVATE = new int[]{ 80 Color.parseColor("#ff26a69a"), 81 Color.parseColor("#ffab47bc"), 82 Color.parseColor("#fff2a600"), 83 Color.parseColor("#ffec407a"), 84 Color.parseColor("#ffc0ca33"), 85 }; 86 87 private StorageManager mStorageManager; 88 89 private PreferenceCategory mInternalCategory; 90 private PreferenceCategory mExternalCategory; 91 92 private StorageSummaryPreference mInternalSummary; 93 private static long sTotalInternalStorage; 94 95 private boolean mHasLaunchedPrivateVolumeSettings = false; 96 97 @Override 98 public int getMetricsCategory() { 99 return METRICS_CATEGORY; 100 } 101 102 @Override 103 public int getHelpResource() { 104 return R.string.help_uri_storage; 105 } 106 107 @Override 108 public void onCreate(Bundle icicle) { 109 super.onCreate(icicle); 110 111 final Context context = getActivity(); 112 113 mStorageManager = context.getSystemService(StorageManager.class); 114 115 if (sTotalInternalStorage <= 0) { 116 sTotalInternalStorage = mStorageManager.getPrimaryStorageSize(); 117 } 118 119 addPreferencesFromResource(R.xml.device_info_storage); 120 121 mInternalCategory = (PreferenceCategory) findPreference("storage_internal"); 122 mExternalCategory = (PreferenceCategory) findPreference("storage_external"); 123 124 mInternalSummary = new StorageSummaryPreference(getPrefContext()); 125 126 setHasOptionsMenu(true); 127 } 128 129 private final StorageEventListener mStorageListener = new StorageEventListener() { 130 @Override 131 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 132 if (isInteresting(vol)) { 133 refresh(); 134 } 135 } 136 137 @Override 138 public void onDiskDestroyed(DiskInfo disk) { 139 refresh(); 140 } 141 }; 142 143 private static boolean isInteresting(VolumeInfo vol) { 144 switch (vol.getType()) { 145 case VolumeInfo.TYPE_PRIVATE: 146 case VolumeInfo.TYPE_PUBLIC: 147 return true; 148 default: 149 return false; 150 } 151 } 152 153 private synchronized void refresh() { 154 final Context context = getPrefContext(); 155 156 getPreferenceScreen().removeAll(); 157 mInternalCategory.removeAll(); 158 mExternalCategory.removeAll(); 159 160 mInternalCategory.addPreference(mInternalSummary); 161 162 int privateCount = 0; 163 164 final StorageManagerVolumeProvider smvp = new StorageManagerVolumeProvider(mStorageManager); 165 final PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo(smvp); 166 final long privateTotalBytes = info.totalBytes; 167 final long privateUsedBytes = info.totalBytes - info.freeBytes; 168 169 final List<VolumeInfo> volumes = mStorageManager.getVolumes(); 170 Collections.sort(volumes, VolumeInfo.getDescriptionComparator()); 171 172 for (VolumeInfo vol : volumes) { 173 if (vol.getType() == VolumeInfo.TYPE_PRIVATE) { 174 final long volumeTotalBytes = PrivateStorageInfo.getTotalSize(vol, 175 sTotalInternalStorage); 176 final int color = COLOR_PRIVATE[privateCount++ % COLOR_PRIVATE.length]; 177 mInternalCategory.addPreference( 178 new StorageVolumePreference(context, vol, color, volumeTotalBytes)); 179 } else if (vol.getType() == VolumeInfo.TYPE_PUBLIC) { 180 mExternalCategory.addPreference( 181 new StorageVolumePreference(context, vol, COLOR_PUBLIC, 0)); 182 } 183 } 184 185 // Show missing private volumes 186 final List<VolumeRecord> recs = mStorageManager.getVolumeRecords(); 187 for (VolumeRecord rec : recs) { 188 if (rec.getType() == VolumeInfo.TYPE_PRIVATE 189 && mStorageManager.findVolumeByUuid(rec.getFsUuid()) == null) { 190 // TODO: add actual storage type to record 191 final Drawable icon = context.getDrawable(R.drawable.ic_sim_sd); 192 icon.mutate(); 193 icon.setTint(COLOR_PUBLIC); 194 195 final Preference pref = new Preference(context); 196 pref.setKey(rec.getFsUuid()); 197 pref.setTitle(rec.getNickname()); 198 pref.setSummary(com.android.internal.R.string.ext_media_status_missing); 199 pref.setIcon(icon); 200 mInternalCategory.addPreference(pref); 201 } 202 } 203 204 // Show unsupported disks to give a chance to init 205 final List<DiskInfo> disks = mStorageManager.getDisks(); 206 for (DiskInfo disk : disks) { 207 if (disk.volumeCount == 0 && disk.size > 0) { 208 final Preference pref = new Preference(context); 209 pref.setKey(disk.getId()); 210 pref.setTitle(disk.getDescription()); 211 pref.setSummary(com.android.internal.R.string.ext_media_status_unsupported); 212 pref.setIcon(R.drawable.ic_sim_sd); 213 mExternalCategory.addPreference(pref); 214 } 215 } 216 217 final BytesResult result = Formatter.formatBytes(getResources(), privateUsedBytes, 0); 218 mInternalSummary.setTitle(TextUtils.expandTemplate(getText(R.string.storage_size_large), 219 result.value, result.units)); 220 mInternalSummary.setSummary(getString(R.string.storage_volume_used_total, 221 Formatter.formatFileSize(context, privateTotalBytes))); 222 if (mInternalCategory.getPreferenceCount() > 0) { 223 getPreferenceScreen().addPreference(mInternalCategory); 224 } 225 if (mExternalCategory.getPreferenceCount() > 0) { 226 getPreferenceScreen().addPreference(mExternalCategory); 227 } 228 229 if (mInternalCategory.getPreferenceCount() == 2 230 && mExternalCategory.getPreferenceCount() == 0) { 231 // Only showing primary internal storage, so just shortcut 232 if (!mHasLaunchedPrivateVolumeSettings) { 233 mHasLaunchedPrivateVolumeSettings = true; 234 final Bundle args = new Bundle(); 235 args.putString(VolumeInfo.EXTRA_VOLUME_ID, VolumeInfo.ID_PRIVATE_INTERNAL); 236 new SubSettingLauncher(getActivity()) 237 .setDestination(StorageDashboardFragment.class.getName()) 238 .setArguments(args) 239 .setTitle(R.string.storage_settings) 240 .setSourceMetricsCategory(getMetricsCategory()) 241 .launch(); 242 finish(); 243 } 244 } 245 } 246 247 @Override 248 public void onResume() { 249 super.onResume(); 250 mStorageManager.registerListener(mStorageListener); 251 refresh(); 252 } 253 254 @Override 255 public void onPause() { 256 super.onPause(); 257 mStorageManager.unregisterListener(mStorageListener); 258 } 259 260 @Override 261 public boolean onPreferenceTreeClick(Preference pref) { 262 final String key = pref.getKey(); 263 if (pref instanceof StorageVolumePreference) { 264 // Picked a normal volume 265 final VolumeInfo vol = mStorageManager.findVolumeById(key); 266 267 if (vol == null) { 268 return false; 269 } 270 271 if (vol.getState() == VolumeInfo.STATE_UNMOUNTED) { 272 VolumeUnmountedFragment.show(this, vol.getId()); 273 return true; 274 } else if (vol.getState() == VolumeInfo.STATE_UNMOUNTABLE) { 275 DiskInitFragment.show(this, R.string.storage_dialog_unmountable, vol.getDiskId()); 276 return true; 277 } 278 279 if (vol.getType() == VolumeInfo.TYPE_PRIVATE) { 280 final Bundle args = new Bundle(); 281 args.putString(VolumeInfo.EXTRA_VOLUME_ID, vol.getId()); 282 283 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { 284 new SubSettingLauncher(getContext()) 285 .setDestination(StorageDashboardFragment.class.getCanonicalName()) 286 .setTitle(R.string.storage_settings) 287 .setSourceMetricsCategory(getMetricsCategory()) 288 .setArguments(args) 289 .launch(); 290 } else { 291 // TODO: Go to the StorageDashboardFragment once it fully handles all of the 292 // SD card cases and other private internal storage cases. 293 PrivateVolumeSettings.setVolumeSize(args, PrivateStorageInfo.getTotalSize(vol, 294 sTotalInternalStorage)); 295 new SubSettingLauncher(getContext()) 296 .setDestination(PrivateVolumeSettings.class.getCanonicalName()) 297 .setTitle(-1) 298 .setSourceMetricsCategory(getMetricsCategory()) 299 .setArguments(args) 300 .launch(); 301 } 302 303 return true; 304 305 } else if (vol.getType() == VolumeInfo.TYPE_PUBLIC) { 306 return handlePublicVolumeClick(getContext(), vol); 307 } 308 309 } else if (key.startsWith("disk:")) { 310 // Picked an unsupported disk 311 DiskInitFragment.show(this, R.string.storage_dialog_unsupported, key); 312 return true; 313 314 } else { 315 // Picked a missing private volume 316 final Bundle args = new Bundle(); 317 args.putString(VolumeRecord.EXTRA_FS_UUID, key); 318 new SubSettingLauncher(getContext()) 319 .setDestination(PrivateVolumeForget.class.getCanonicalName()) 320 .setTitle(R.string.storage_menu_forget) 321 .setSourceMetricsCategory(getMetricsCategory()) 322 .setArguments(args) 323 .launch(); 324 return true; 325 } 326 327 return false; 328 } 329 330 @VisibleForTesting 331 static boolean handlePublicVolumeClick(Context context, VolumeInfo vol) { 332 final Intent intent = vol.buildBrowseIntent(); 333 if (vol.isMountedReadable() && intent != null) { 334 context.startActivity(intent); 335 return true; 336 } else { 337 final Bundle args = new Bundle(); 338 args.putString(VolumeInfo.EXTRA_VOLUME_ID, vol.getId()); 339 new SubSettingLauncher(context) 340 .setDestination(PublicVolumeSettings.class.getCanonicalName()) 341 .setTitle(-1) 342 .setSourceMetricsCategory(METRICS_CATEGORY) 343 .setArguments(args) 344 .launch(); 345 return true; 346 } 347 } 348 349 public static class MountTask extends AsyncTask<Void, Void, Exception> { 350 private final Context mContext; 351 private final StorageManager mStorageManager; 352 private final String mVolumeId; 353 private final String mDescription; 354 355 public MountTask(Context context, VolumeInfo volume) { 356 mContext = context.getApplicationContext(); 357 mStorageManager = mContext.getSystemService(StorageManager.class); 358 mVolumeId = volume.getId(); 359 mDescription = mStorageManager.getBestVolumeDescription(volume); 360 } 361 362 @Override 363 protected Exception doInBackground(Void... params) { 364 try { 365 mStorageManager.mount(mVolumeId); 366 return null; 367 } catch (Exception e) { 368 return e; 369 } 370 } 371 372 @Override 373 protected void onPostExecute(Exception e) { 374 if (e == null) { 375 Toast.makeText(mContext, mContext.getString(R.string.storage_mount_success, 376 mDescription), Toast.LENGTH_SHORT).show(); 377 } else { 378 Log.e(TAG, "Failed to mount " + mVolumeId, e); 379 Toast.makeText(mContext, mContext.getString(R.string.storage_mount_failure, 380 mDescription), Toast.LENGTH_SHORT).show(); 381 } 382 } 383 } 384 385 public static class UnmountTask extends AsyncTask<Void, Void, Exception> { 386 private final Context mContext; 387 private final StorageManager mStorageManager; 388 private final String mVolumeId; 389 private final String mDescription; 390 391 public UnmountTask(Context context, VolumeInfo volume) { 392 mContext = context.getApplicationContext(); 393 mStorageManager = mContext.getSystemService(StorageManager.class); 394 mVolumeId = volume.getId(); 395 mDescription = mStorageManager.getBestVolumeDescription(volume); 396 } 397 398 @Override 399 protected Exception doInBackground(Void... params) { 400 try { 401 mStorageManager.unmount(mVolumeId); 402 return null; 403 } catch (Exception e) { 404 return e; 405 } 406 } 407 408 @Override 409 protected void onPostExecute(Exception e) { 410 if (e == null) { 411 Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_success, 412 mDescription), Toast.LENGTH_SHORT).show(); 413 } else { 414 Log.e(TAG, "Failed to unmount " + mVolumeId, e); 415 Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_failure, 416 mDescription), Toast.LENGTH_SHORT).show(); 417 } 418 } 419 } 420 421 public static class VolumeUnmountedFragment extends InstrumentedDialogFragment { 422 public static void show(Fragment parent, String volumeId) { 423 final Bundle args = new Bundle(); 424 args.putString(VolumeInfo.EXTRA_VOLUME_ID, volumeId); 425 426 final VolumeUnmountedFragment dialog = new VolumeUnmountedFragment(); 427 dialog.setArguments(args); 428 dialog.setTargetFragment(parent, 0); 429 dialog.show(parent.getFragmentManager(), TAG_VOLUME_UNMOUNTED); 430 } 431 432 @Override 433 public int getMetricsCategory() { 434 return MetricsEvent.DIALOG_VOLUME_UNMOUNT; 435 } 436 437 @Override 438 public Dialog onCreateDialog(Bundle savedInstanceState) { 439 final Context context = getActivity(); 440 final StorageManager sm = context.getSystemService(StorageManager.class); 441 442 final String volumeId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID); 443 final VolumeInfo vol = sm.findVolumeById(volumeId); 444 445 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 446 builder.setMessage(TextUtils.expandTemplate( 447 getText(R.string.storage_dialog_unmounted), vol.getDisk().getDescription())); 448 449 builder.setPositiveButton(R.string.storage_menu_mount, 450 new DialogInterface.OnClickListener() { 451 /** 452 * Check if an {@link 453 * RestrictedLockUtils#sendShowAdminSupportDetailsIntent admin 454 * details intent} should be shown for the restriction and show it. 455 * 456 * @param restriction The restriction to check 457 * @return {@code true} iff a intent was shown. 458 */ 459 private boolean wasAdminSupportIntentShown(@NonNull String restriction) { 460 EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced( 461 getActivity(), restriction, UserHandle.myUserId()); 462 boolean hasBaseUserRestriction = 463 RestrictedLockUtils.hasBaseUserRestriction( 464 getActivity(), restriction, UserHandle.myUserId()); 465 if (admin != null && !hasBaseUserRestriction) { 466 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), 467 admin); 468 return true; 469 } 470 471 return false; 472 } 473 474 @Override 475 public void onClick(DialogInterface dialog, int which) { 476 if (wasAdminSupportIntentShown( 477 UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) { 478 return; 479 } 480 481 if (vol.disk != null && vol.disk.isUsb() && 482 wasAdminSupportIntentShown( 483 UserManager.DISALLOW_USB_FILE_TRANSFER)) { 484 return; 485 } 486 487 new MountTask(context, vol).execute(); 488 } 489 }); 490 builder.setNegativeButton(R.string.cancel, null); 491 492 return builder.create(); 493 } 494 } 495 496 public static class DiskInitFragment extends InstrumentedDialogFragment { 497 @Override 498 public int getMetricsCategory() { 499 return MetricsEvent.DIALOG_VOLUME_INIT; 500 } 501 502 public static void show(Fragment parent, int resId, String diskId) { 503 final Bundle args = new Bundle(); 504 args.putInt(Intent.EXTRA_TEXT, resId); 505 args.putString(DiskInfo.EXTRA_DISK_ID, diskId); 506 507 final DiskInitFragment dialog = new DiskInitFragment(); 508 dialog.setArguments(args); 509 dialog.setTargetFragment(parent, 0); 510 dialog.show(parent.getFragmentManager(), TAG_DISK_INIT); 511 } 512 513 @Override 514 public Dialog onCreateDialog(Bundle savedInstanceState) { 515 final Context context = getActivity(); 516 final StorageManager sm = context.getSystemService(StorageManager.class); 517 518 final int resId = getArguments().getInt(Intent.EXTRA_TEXT); 519 final String diskId = getArguments().getString(DiskInfo.EXTRA_DISK_ID); 520 final DiskInfo disk = sm.findDiskById(diskId); 521 522 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 523 builder.setMessage(TextUtils.expandTemplate(getText(resId), disk.getDescription())); 524 525 builder.setPositiveButton(R.string.storage_menu_set_up, 526 new DialogInterface.OnClickListener() { 527 @Override 528 public void onClick(DialogInterface dialog, int which) { 529 final Intent intent = new Intent(context, StorageWizardInit.class); 530 intent.putExtra(DiskInfo.EXTRA_DISK_ID, diskId); 531 startActivity(intent); 532 } 533 }); 534 builder.setNegativeButton(R.string.cancel, null); 535 536 return builder.create(); 537 } 538 } 539 540 private static class SummaryProvider implements SummaryLoader.SummaryProvider { 541 private final Context mContext; 542 private final SummaryLoader mLoader; 543 private final StorageManagerVolumeProvider mStorageManagerVolumeProvider; 544 545 private SummaryProvider(Context context, SummaryLoader loader) { 546 mContext = context; 547 mLoader = loader; 548 final StorageManager storageManager = mContext.getSystemService(StorageManager.class); 549 mStorageManagerVolumeProvider = new StorageManagerVolumeProvider(storageManager); 550 } 551 552 @Override 553 public void setListening(boolean listening) { 554 if (listening) { 555 updateSummary(); 556 } 557 } 558 559 private void updateSummary() { 560 // TODO: Register listener. 561 final NumberFormat percentageFormat = NumberFormat.getPercentInstance(); 562 final PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo( 563 mStorageManagerVolumeProvider); 564 double privateUsedBytes = info.totalBytes - info.freeBytes; 565 mLoader.setSummary(this, mContext.getString(R.string.storage_summary, 566 percentageFormat.format(privateUsedBytes / info.totalBytes), 567 Formatter.formatFileSize(mContext, info.freeBytes))); 568 } 569 } 570 571 572 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY 573 = (activity, summaryLoader) -> new SummaryProvider(activity, summaryLoader); 574 575 /** Enable indexing of searchable data */ 576 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 577 new BaseSearchIndexProvider() { 578 @Override 579 public List<SearchIndexableRaw> getRawDataToIndex( 580 Context context, boolean enabled) { 581 final List<SearchIndexableRaw> result = new ArrayList<>(); 582 583 SearchIndexableRaw data = new SearchIndexableRaw(context); 584 data.title = context.getString(R.string.storage_settings); 585 data.key = "storage_settings"; 586 data.screenTitle = context.getString(R.string.storage_settings); 587 data.keywords = context.getString(R.string.keywords_storage_settings); 588 result.add(data); 589 590 data = new SearchIndexableRaw(context); 591 data.title = context.getString(R.string.internal_storage); 592 data.key = "storage_settings_internal_storage"; 593 data.screenTitle = context.getString(R.string.storage_settings); 594 result.add(data); 595 596 data = new SearchIndexableRaw(context); 597 final StorageManager storage = context.getSystemService(StorageManager.class); 598 final List<VolumeInfo> vols = storage.getVolumes(); 599 for (VolumeInfo vol : vols) { 600 if (isInteresting(vol)) { 601 data.title = storage.getBestVolumeDescription(vol); 602 data.key = "storage_settings_volume_" + vol.id; 603 data.screenTitle = context.getString(R.string.storage_settings); 604 result.add(data); 605 } 606 } 607 608 data = new SearchIndexableRaw(context); 609 data.title = context.getString(R.string.memory_size); 610 data.key = "storage_settings_memory_size"; 611 data.screenTitle = context.getString(R.string.storage_settings); 612 result.add(data); 613 614 data = new SearchIndexableRaw(context); 615 data.title = context.getString(R.string.memory_available); 616 data.key = "storage_settings_memory_available"; 617 data.screenTitle = context.getString(R.string.storage_settings); 618 result.add(data); 619 620 data = new SearchIndexableRaw(context); 621 data.title = context.getString(R.string.memory_apps_usage); 622 data.key = "storage_settings_apps_space"; 623 data.screenTitle = context.getString(R.string.storage_settings); 624 result.add(data); 625 626 data = new SearchIndexableRaw(context); 627 data.title = context.getString(R.string.memory_dcim_usage); 628 data.key = "storage_settings_dcim_space"; 629 data.screenTitle = context.getString(R.string.storage_settings); 630 result.add(data); 631 632 data = new SearchIndexableRaw(context); 633 data.title = context.getString(R.string.memory_music_usage); 634 data.key = "storage_settings_music_space"; 635 data.screenTitle = context.getString(R.string.storage_settings); 636 result.add(data); 637 638 data = new SearchIndexableRaw(context); 639 data.title = context.getString(R.string.memory_media_misc_usage); 640 data.key = "storage_settings_misc_space"; 641 data.screenTitle = context.getString(R.string.storage_settings); 642 result.add(data); 643 644 data = new SearchIndexableRaw(context); 645 data.title = context.getString(R.string.storage_menu_free); 646 data.key = "storage_settings_free_space"; 647 data.screenTitle = context.getString(R.string.storage_menu_free); 648 // We need to define all three in order for this to trigger properly. 649 data.intentAction = StorageManager.ACTION_MANAGE_STORAGE; 650 data.intentTargetPackage = 651 context.getString(R.string.config_deletion_helper_package); 652 data.intentTargetClass = 653 context.getString(R.string.config_deletion_helper_class); 654 result.add(data); 655 656 return result; 657 } 658 }; 659 } 660