• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.storage;
18 
19 import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.PERSONAL_TAB;
20 import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.PRIVATE_TAB;
21 import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.WORK_TAB;
22 
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.content.pm.UserInfo;
27 import android.content.res.TypedArray;
28 import android.graphics.drawable.Drawable;
29 import android.net.Uri;
30 import android.os.Build;
31 import android.os.Bundle;
32 import android.os.UserHandle;
33 import android.os.UserManager;
34 import android.os.storage.VolumeInfo;
35 import android.util.DataUnit;
36 import android.util.Log;
37 import android.util.SparseArray;
38 import android.widget.Toast;
39 
40 import androidx.annotation.Nullable;
41 import androidx.annotation.VisibleForTesting;
42 import androidx.fragment.app.Fragment;
43 import androidx.preference.Preference;
44 import androidx.preference.PreferenceCategory;
45 import androidx.preference.PreferenceScreen;
46 
47 import com.android.settings.R;
48 import com.android.settings.Settings;
49 import com.android.settings.SettingsActivity;
50 import com.android.settings.Utils;
51 import com.android.settings.applications.manageapplications.ManageApplications;
52 import com.android.settings.core.PreferenceControllerMixin;
53 import com.android.settings.core.SubSettingLauncher;
54 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
55 import com.android.settings.deviceinfo.StorageItemPreference;
56 import com.android.settings.deviceinfo.storage.StorageUtils.SystemInfoFragment;
57 import com.android.settings.deviceinfo.storage.StorageUtils.TemporaryFilesInfoFragment;
58 import com.android.settings.overlay.FeatureFactory;
59 import com.android.settingslib.core.AbstractPreferenceController;
60 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
61 import com.android.settingslib.deviceinfo.StorageMeasurement;
62 import com.android.settingslib.deviceinfo.StorageVolumeProvider;
63 
64 import java.util.ArrayList;
65 import java.util.Collections;
66 import java.util.Comparator;
67 import java.util.List;
68 import java.util.Map;
69 
70 /**
71  * StorageItemPreferenceController handles the storage line items which summarize the storage
72  * categorization breakdown.
73  */
74 public class StorageItemPreferenceController extends AbstractPreferenceController implements
75         PreferenceControllerMixin,
76         EmptyTrashFragment.OnEmptyTrashCompleteListener {
77     private static final String TAG = "StorageItemPreference";
78 
79     private static final String SYSTEM_FRAGMENT_TAG = "SystemInfo";
80     private static final String TEMPORARY_FILES_FRAGMENT_TAG = "TemporaryFilesInfo";
81 
82     @VisibleForTesting
83     static final String PUBLIC_STORAGE_KEY = "pref_public_storage";
84     @VisibleForTesting
85     static final String IMAGES_KEY = "pref_images";
86     @VisibleForTesting
87     static final String VIDEOS_KEY = "pref_videos";
88     @VisibleForTesting
89     static final String AUDIO_KEY = "pref_audio";
90     @VisibleForTesting
91     static final String APPS_KEY = "pref_apps";
92     @VisibleForTesting
93     static final String GAMES_KEY = "pref_games";
94     @VisibleForTesting
95     static final String DOCUMENTS_KEY = "pref_documents";
96     @VisibleForTesting
97     static final String OTHER_KEY = "pref_other";
98     @VisibleForTesting
99     static final String SYSTEM_KEY = "pref_system";
100     @VisibleForTesting
101     static final String TEMPORARY_FILES_KEY = "temporary_files";
102     @VisibleForTesting
103     static final String CATEGORY_SPLITTER = "storage_category_splitter";
104     @VisibleForTesting
105     static final String TRASH_KEY = "pref_trash";
106 
107     @VisibleForTesting
108     final Uri mImagesUri;
109     @VisibleForTesting
110     final Uri mVideosUri;
111     @VisibleForTesting
112     final Uri mAudioUri;
113     @VisibleForTesting
114     final Uri mDocumentsUri;
115     @VisibleForTesting
116     final Uri mOtherUri;
117 
118     // This value should align with the design of storage_dashboard_fragment.xml
119     private static final int LAST_STORAGE_CATEGORY_PREFERENCE_ORDER = 200;
120 
121     private PackageManager mPackageManager;
122     private UserManager mUserManager;
123     private final Fragment mFragment;
124     private final MetricsFeatureProvider mMetricsFeatureProvider;
125     private final StorageVolumeProvider mSvp;
126     @Nullable private VolumeInfo mVolume;
127     private int mUserId;
128     private long mUsedBytes;
129     private long mTotalSize;
130 
131     @Nullable private List<StorageItemPreference> mPrivateStorageItemPreferences;
132     @Nullable private PreferenceScreen mScreen;
133     @VisibleForTesting
134     @Nullable Preference mPublicStoragePreference;
135     @VisibleForTesting
136     @Nullable StorageItemPreference mImagesPreference;
137     @VisibleForTesting
138     @Nullable StorageItemPreference mVideosPreference;
139     @VisibleForTesting
140     @Nullable StorageItemPreference mAudioPreference;
141     @VisibleForTesting
142     @Nullable StorageItemPreference mAppsPreference;
143     @VisibleForTesting
144     @Nullable StorageItemPreference mGamesPreference;
145     @VisibleForTesting
146     @Nullable StorageItemPreference mDocumentsPreference;
147     @VisibleForTesting
148     @Nullable StorageItemPreference mOtherPreference;
149     @VisibleForTesting
150     @Nullable StorageItemPreference mTrashPreference;
151     @VisibleForTesting
152     @Nullable StorageItemPreference mSystemPreference;
153     @VisibleForTesting
154     @Nullable StorageItemPreference mTemporaryFilesPreference;
155     @VisibleForTesting
156     @Nullable PreferenceCategory mCategorySplitterPreferenceCategory;
157 
158     private final int mProfileType;
159 
160     private StorageCacheHelper mStorageCacheHelper;
161     // The mIsDocumentsPrefShown being used here is to prevent a flicker problem from displaying
162     // the Document entry.
163     private boolean mIsDocumentsPrefShown;
164     private boolean mIsPreferenceOrderedBySize;
165 
StorageItemPreferenceController( Context context, Fragment hostFragment, VolumeInfo volume, StorageVolumeProvider svp)166     public StorageItemPreferenceController(
167             Context context, Fragment hostFragment, VolumeInfo volume, StorageVolumeProvider svp) {
168         this(context, hostFragment, volume, svp, ProfileSelectFragment.ProfileType.PERSONAL);
169     }
170 
StorageItemPreferenceController( Context context, Fragment hostFragment, @Nullable VolumeInfo volume, StorageVolumeProvider svp, @ProfileSelectFragment.ProfileType int profileType)171     public StorageItemPreferenceController(
172             Context context,
173             Fragment hostFragment,
174             @Nullable VolumeInfo volume,
175             StorageVolumeProvider svp,
176             @ProfileSelectFragment.ProfileType int profileType) {
177         super(context);
178         mPackageManager = context.getPackageManager();
179         mUserManager = context.getSystemService(UserManager.class);
180         mFragment = hostFragment;
181         mVolume = volume;
182         mSvp = svp;
183         mProfileType = profileType;
184         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
185         mUserId = getCurrentUserId();
186         mIsDocumentsPrefShown = isDocumentsPrefShown();
187         mStorageCacheHelper = new StorageCacheHelper(mContext, mUserId);
188 
189         mImagesUri = Uri.parse(context.getResources()
190                 .getString(R.string.config_images_storage_category_uri));
191         mVideosUri = Uri.parse(context.getResources()
192                 .getString(R.string.config_videos_storage_category_uri));
193         mAudioUri = Uri.parse(context.getResources()
194                 .getString(R.string.config_audio_storage_category_uri));
195         mDocumentsUri = Uri.parse(context.getResources()
196                 .getString(R.string.config_documents_storage_category_uri));
197         mOtherUri = Uri.parse(context.getResources()
198                 .getString(R.string.config_other_storage_category_uri));
199     }
200 
201     @VisibleForTesting
getCurrentUserId()202     int getCurrentUserId() {
203         return Utils.getCurrentUserIdOfType(mUserManager, mProfileType);
204     }
205 
206     @Override
isAvailable()207     public boolean isAvailable() {
208         return true;
209     }
210 
211     @Override
handlePreferenceTreeClick(Preference preference)212     public boolean handlePreferenceTreeClick(Preference preference) {
213         if (preference.getKey() == null) {
214             return false;
215         }
216         switch (preference.getKey()) {
217             case PUBLIC_STORAGE_KEY:
218                 launchPublicStorageIntent();
219                 return true;
220             case IMAGES_KEY:
221                 launchActivityWithUri(mImagesUri);
222                 return true;
223             case VIDEOS_KEY:
224                 launchActivityWithUri(mVideosUri);
225                 return true;
226             case AUDIO_KEY:
227                 launchActivityWithUri(mAudioUri);
228                 return true;
229             case APPS_KEY:
230                 launchAppsIntent();
231                 return true;
232             case GAMES_KEY:
233                 launchGamesIntent();
234                 return true;
235             case DOCUMENTS_KEY:
236                 launchActivityWithUri(mDocumentsUri);
237                 return true;
238             case OTHER_KEY:
239                 launchActivityWithUri(mOtherUri);
240                 return true;
241             case SYSTEM_KEY:
242                 final SystemInfoFragment dialog = new SystemInfoFragment();
243                 dialog.setTargetFragment(mFragment, 0);
244                 dialog.show(mFragment.getFragmentManager(), SYSTEM_FRAGMENT_TAG);
245                 return true;
246             case TEMPORARY_FILES_KEY:
247                 final TemporaryFilesInfoFragment temporaryFilesDialog =
248                         new TemporaryFilesInfoFragment();
249                 temporaryFilesDialog.setTargetFragment(mFragment, 0);
250                 temporaryFilesDialog.show(mFragment.getFragmentManager(),
251                         TEMPORARY_FILES_FRAGMENT_TAG);
252                 return true;
253             case TRASH_KEY:
254                 launchTrashIntent();
255                 return true;
256             default:
257                 // Do nothing.
258         }
259         return super.handlePreferenceTreeClick(preference);
260     }
261 
262     @Override
getPreferenceKey()263     public String getPreferenceKey() {
264         return null;
265     }
266 
267     /**
268      * Sets the storage volume to use for when handling taps.
269      */
setVolume(VolumeInfo volume)270     public void setVolume(VolumeInfo volume) {
271         mVolume = volume;
272 
273         if (mPublicStoragePreference != null) {
274             mPublicStoragePreference.setVisible(
275                     isValidPublicVolume()
276                             && mProfileType == ProfileSelectFragment.ProfileType.PERSONAL);
277         }
278 
279         // If isValidPrivateVolume() is true, these preferences will become visible at
280         // onLoadFinished.
281         if (isValidPrivateVolume()) {
282             mIsDocumentsPrefShown = isDocumentsPrefShown();
283         } else {
284             setPrivateStorageCategoryPreferencesVisibility(false);
285         }
286     }
287 
288     // Stats data is only available on private volumes.
isValidPrivateVolume()289     private boolean isValidPrivateVolume() {
290         return mVolume != null
291                 && mVolume.getType() == VolumeInfo.TYPE_PRIVATE
292                 && (mVolume.getState() == VolumeInfo.STATE_MOUNTED
293                 || mVolume.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);
294     }
295 
isValidPublicVolume()296     private boolean isValidPublicVolume() {
297         // Stub volume is a volume that is maintained by external party such as the ChromeOS
298         // processes in ARC++.
299         return mVolume != null
300                 && (mVolume.getType() == VolumeInfo.TYPE_PUBLIC
301                 || mVolume.getType() == VolumeInfo.TYPE_STUB)
302                 && (mVolume.getState() == VolumeInfo.STATE_MOUNTED
303                 || mVolume.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);
304     }
305 
306     @VisibleForTesting
setPrivateStorageCategoryPreferencesVisibility(boolean visible)307     void setPrivateStorageCategoryPreferencesVisibility(boolean visible) {
308         if (mScreen == null) {
309             return;
310         }
311 
312         mImagesPreference.setVisible(visible);
313         mVideosPreference.setVisible(visible);
314         mAudioPreference.setVisible(visible);
315         mAppsPreference.setVisible(visible);
316         mGamesPreference.setVisible(visible);
317         mSystemPreference.setVisible(visible);
318         mTemporaryFilesPreference.setVisible(visible);
319         mCategorySplitterPreferenceCategory.setVisible(visible);
320         mTrashPreference.setVisible(visible);
321 
322         // If we don't have a shared volume for our internal storage (or the shared volume isn't
323         // mounted as readable for whatever reason), we should hide the File preference.
324         if (visible) {
325             mDocumentsPreference.setVisible(mIsDocumentsPrefShown);
326             mOtherPreference.setVisible(mIsDocumentsPrefShown);
327         } else {
328             mDocumentsPreference.setVisible(false);
329             mOtherPreference.setVisible(false);
330         }
331     }
332 
isDocumentsPrefShown()333     private boolean isDocumentsPrefShown() {
334         VolumeInfo sharedVolume = mSvp.findEmulatedForPrivate(mVolume);
335         return sharedVolume != null && sharedVolume.isMountedReadable();
336     }
337 
updatePrivateStorageCategoryPreferencesOrder()338     private void updatePrivateStorageCategoryPreferencesOrder() {
339         if (mScreen == null || !isValidPrivateVolume()) {
340             return;
341         }
342 
343         if (mPrivateStorageItemPreferences == null) {
344             mPrivateStorageItemPreferences = new ArrayList<>();
345 
346             // Adding categories in the reverse order so that
347             // They would be in the right order after sorting
348             mPrivateStorageItemPreferences.add(mTrashPreference);
349             mPrivateStorageItemPreferences.add(mOtherPreference);
350             mPrivateStorageItemPreferences.add(mDocumentsPreference);
351             mPrivateStorageItemPreferences.add(mGamesPreference);
352             mPrivateStorageItemPreferences.add(mAppsPreference);
353             mPrivateStorageItemPreferences.add(mAudioPreference);
354             mPrivateStorageItemPreferences.add(mVideosPreference);
355             mPrivateStorageItemPreferences.add(mImagesPreference);
356         }
357         mScreen.removePreference(mImagesPreference);
358         mScreen.removePreference(mVideosPreference);
359         mScreen.removePreference(mAudioPreference);
360         mScreen.removePreference(mAppsPreference);
361         mScreen.removePreference(mGamesPreference);
362         mScreen.removePreference(mDocumentsPreference);
363         mScreen.removePreference(mOtherPreference);
364         mScreen.removePreference(mTrashPreference);
365 
366         // Sort display order by size.
367         Collections.sort(mPrivateStorageItemPreferences,
368                 Comparator.comparingLong(StorageItemPreference::getStorageSize));
369         int orderIndex = LAST_STORAGE_CATEGORY_PREFERENCE_ORDER;
370         for (StorageItemPreference preference : mPrivateStorageItemPreferences) {
371             preference.setOrder(orderIndex--);
372             mScreen.addPreference(preference);
373         }
374     }
375 
376     /**
377      * Sets the user id for which this preference controller is handling.
378      */
setUserId(UserHandle userHandle)379     public void setUserId(UserHandle userHandle) {
380         if (mProfileType == ProfileSelectFragment.ProfileType.WORK
381                 && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
382             throw new IllegalArgumentException("Only accept work profile userHandle");
383         }
384 
385         UserInfo userInfo = mUserManager.getUserInfo(userHandle.getIdentifier());
386         if (mProfileType == ProfileSelectFragment.ProfileType.PRIVATE
387                 && (userInfo == null || userInfo.isPrivateProfile())) {
388             throw new IllegalArgumentException("Only accept private profile userHandle");
389         }
390         mUserId = userHandle.getIdentifier();
391 
392         tintPreference(mPublicStoragePreference);
393         tintPreference(mImagesPreference);
394         tintPreference(mVideosPreference);
395         tintPreference(mAudioPreference);
396         tintPreference(mAppsPreference);
397         tintPreference(mGamesPreference);
398         tintPreference(mDocumentsPreference);
399         tintPreference(mOtherPreference);
400         tintPreference(mSystemPreference);
401         tintPreference(mTemporaryFilesPreference);
402         tintPreference(mTrashPreference);
403     }
404 
tintPreference(Preference preference)405     private void tintPreference(Preference preference) {
406         if (preference != null) {
407             preference.setIcon(applyTint(mContext, preference.getIcon()));
408         }
409     }
410 
applyTint(Context context, Drawable icon)411     private static Drawable applyTint(Context context, Drawable icon) {
412         TypedArray array =
413                 context.obtainStyledAttributes(new int[]{android.R.attr.colorControlNormal});
414         icon = icon.mutate();
415         icon.setTint(array.getColor(0, 0));
416         array.recycle();
417         return icon;
418     }
419 
420     @Override
displayPreference(PreferenceScreen screen)421     public void displayPreference(PreferenceScreen screen) {
422         mScreen = screen;
423         mPublicStoragePreference = screen.findPreference(PUBLIC_STORAGE_KEY);
424         mImagesPreference = screen.findPreference(IMAGES_KEY);
425         mVideosPreference = screen.findPreference(VIDEOS_KEY);
426         mAudioPreference = screen.findPreference(AUDIO_KEY);
427         mAppsPreference = screen.findPreference(APPS_KEY);
428         mGamesPreference = screen.findPreference(GAMES_KEY);
429         mDocumentsPreference = screen.findPreference(DOCUMENTS_KEY);
430         mOtherPreference = screen.findPreference(OTHER_KEY);
431         mCategorySplitterPreferenceCategory = screen.findPreference(CATEGORY_SPLITTER);
432         mSystemPreference = screen.findPreference(SYSTEM_KEY);
433         mTemporaryFilesPreference = screen.findPreference(TEMPORARY_FILES_KEY);
434         mTrashPreference = screen.findPreference(TRASH_KEY);
435     }
436 
437     /**
438      * Fragments use it to set storage result and update UI of this controller.
439      * @param result The StorageResult from StorageAsyncLoader. This allows a nullable result.
440      *               When it's null, the cached storage size info will be used instead.
441      * @param userId User ID to get the storage size info
442      */
onLoadFinished(@ullable SparseArray<StorageAsyncLoader.StorageResult> result, int userId)443     public void onLoadFinished(@Nullable SparseArray<StorageAsyncLoader.StorageResult> result,
444             int userId) {
445         // Enable animation when the storage size info is from StorageAsyncLoader whereas disable
446         // animation when the cached storage size info is used instead.
447         boolean animate = result != null && mIsPreferenceOrderedBySize;
448         // Calculate the size info for each category
449         StorageCacheHelper.StorageCache storageCache = getSizeInfo(result, userId);
450         // Set size info to each preference
451         mImagesPreference.setStorageSize(storageCache.imagesSize, mTotalSize, animate);
452         mVideosPreference.setStorageSize(storageCache.videosSize, mTotalSize, animate);
453         mAudioPreference.setStorageSize(storageCache.audioSize, mTotalSize, animate);
454         mAppsPreference.setStorageSize(storageCache.allAppsExceptGamesSize, mTotalSize, animate);
455         mGamesPreference.setStorageSize(storageCache.gamesSize, mTotalSize, animate);
456         mDocumentsPreference.setStorageSize(storageCache.documentsSize, mTotalSize, animate);
457         mOtherPreference.setStorageSize(storageCache.otherSize, mTotalSize, animate);
458         mTrashPreference.setStorageSize(storageCache.trashSize, mTotalSize, animate);
459         if (mSystemPreference != null) {
460             mSystemPreference.setStorageSize(storageCache.systemSize, mTotalSize, animate);
461             mSystemPreference.setTitle(mContext.getString(R.string.storage_os_name,
462                     Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY));
463         }
464         if (mTemporaryFilesPreference != null) {
465             mTemporaryFilesPreference.setStorageSize(storageCache.temporaryFilesSize, mTotalSize,
466                     animate);
467         }
468         // Cache the size info
469         if (result != null) {
470             mStorageCacheHelper.cacheSizeInfo(storageCache);
471         }
472 
473         // Sort the preference according to size info in descending order
474         if (!mIsPreferenceOrderedBySize) {
475             updatePrivateStorageCategoryPreferencesOrder();
476             mIsPreferenceOrderedBySize = true;
477         }
478 
479         if (isValidPrivateVolume()) {
480             setPrivateStorageCategoryPreferencesVisibility(true);
481         }
482     }
483 
getSizeInfo( SparseArray<StorageAsyncLoader.StorageResult> result, int userId)484     private StorageCacheHelper.StorageCache getSizeInfo(
485             SparseArray<StorageAsyncLoader.StorageResult> result, int userId) {
486         if (result == null) {
487             return mStorageCacheHelper.retrieveCachedSize();
488         }
489         StorageAsyncLoader.StorageResult data = result.get(userId);
490         StorageCacheHelper.StorageCache storageCache = new StorageCacheHelper.StorageCache();
491         storageCache.imagesSize = data.imagesSize;
492         storageCache.videosSize = data.videosSize;
493         storageCache.audioSize = data.audioSize;
494         storageCache.allAppsExceptGamesSize = data.allAppsExceptGamesSize;
495         storageCache.gamesSize = data.gamesSize;
496         storageCache.documentsSize = data.documentsSize;
497         storageCache.otherSize = data.otherSize;
498         storageCache.trashSize = data.trashSize;
499         storageCache.systemSize = data.systemSize;
500         // Everything else that hasn't already been attributed is tracked as
501         // belonging to system.
502         long attributedSize = 0;
503         for (int i = 0; i < result.size(); i++) {
504             final StorageAsyncLoader.StorageResult otherData = result.valueAt(i);
505             attributedSize +=
506                     otherData.gamesSize
507                             + otherData.audioSize
508                             + otherData.videosSize
509                             + otherData.imagesSize
510                             + otherData.documentsSize
511                             + otherData.otherSize
512                             + otherData.trashSize
513                             + otherData.allAppsExceptGamesSize;
514             attributedSize -= otherData.duplicateCodeSize;
515         }
516         // System size is equal for each user and should be added only once
517         attributedSize += data.systemSize;
518         storageCache.temporaryFilesSize = Math.max(DataUnit.GIBIBYTES.toBytes(1),
519                 mUsedBytes - attributedSize);
520         return storageCache;
521     }
522 
setUsedSize(long usedSizeBytes)523     public void setUsedSize(long usedSizeBytes) {
524         mUsedBytes = usedSizeBytes;
525     }
526 
setTotalSize(long totalSizeBytes)527     public void setTotalSize(long totalSizeBytes) {
528         mTotalSize = totalSizeBytes;
529     }
530 
launchPublicStorageIntent()531     private void launchPublicStorageIntent() {
532         final Intent intent = mVolume.buildBrowseIntent();
533         if (intent == null) {
534             return;
535         }
536         mContext.startActivityAsUser(intent, new UserHandle(mUserId));
537     }
538 
launchActivityWithUri(Uri dataUri)539     private void launchActivityWithUri(Uri dataUri) {
540         final Intent intent = new Intent(Intent.ACTION_VIEW);
541         intent.setData(dataUri);
542         mContext.startActivityAsUser(intent, new UserHandle(mUserId));
543     }
544 
launchAppsIntent()545     private void launchAppsIntent() {
546         final Bundle args = getWorkAnnotatedBundle(3);
547         args.putString(ManageApplications.EXTRA_CLASSNAME,
548                 Settings.StorageUseActivity.class.getName());
549         args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
550         args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription());
551         final Intent intent = new SubSettingLauncher(mContext)
552                 .setDestination(ManageApplications.class.getName())
553                 .setTitleRes(R.string.apps_storage)
554                 .setArguments(args)
555                 .setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
556                 .toIntent();
557         intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
558         Utils.launchIntent(mFragment, intent);
559     }
560 
launchGamesIntent()561     private void launchGamesIntent() {
562         final Bundle args = getWorkAnnotatedBundle(1);
563         args.putString(ManageApplications.EXTRA_CLASSNAME,
564                 Settings.GamesStorageActivity.class.getName());
565         final Intent intent = new SubSettingLauncher(mContext)
566                 .setDestination(ManageApplications.class.getName())
567                 .setTitleRes(R.string.game_storage_settings)
568                 .setArguments(args)
569                 .setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
570                 .toIntent();
571         intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
572         Utils.launchIntent(mFragment, intent);
573     }
574 
getWorkAnnotatedBundle(int additionalCapacity)575     private Bundle getWorkAnnotatedBundle(int additionalCapacity) {
576         final Bundle args = new Bundle(1 + additionalCapacity);
577         if (mProfileType == ProfileSelectFragment.ProfileType.WORK) {
578             args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, WORK_TAB);
579         } else if (mProfileType == ProfileSelectFragment.ProfileType.PRIVATE) {
580             args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, PRIVATE_TAB);
581         } else {
582             args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, PERSONAL_TAB);
583         }
584         return args;
585     }
586 
launchTrashIntent()587     private void launchTrashIntent() {
588         final Intent intent = new Intent("android.settings.VIEW_TRASH");
589 
590         if (mPackageManager.resolveActivityAsUser(intent, 0 /* flags */, mUserId) == null) {
591             final long trashSize = mTrashPreference.getStorageSize();
592             if (trashSize > 0) {
593                 new EmptyTrashFragment(mFragment, mUserId, trashSize,
594                         this /* onEmptyTrashCompleteListener */).show();
595             } else {
596                 Toast.makeText(mContext, R.string.storage_trash_dialog_empty_message,
597                         Toast.LENGTH_SHORT).show();
598             }
599         } else {
600             mContext.startActivityAsUser(intent, new UserHandle(mUserId));
601         }
602     }
603 
604     @Override
onEmptyTrashComplete()605     public void onEmptyTrashComplete() {
606         if (mTrashPreference == null) {
607             return;
608         }
609         mTrashPreference.setStorageSize(0, mTotalSize, true /* animate */);
610         updatePrivateStorageCategoryPreferencesOrder();
611     }
612 
totalValues(StorageMeasurement.MeasurementDetails details, int userId, String... keys)613     private static long totalValues(StorageMeasurement.MeasurementDetails details, int userId,
614             String... keys) {
615         long total = 0;
616         Map<String, Long> map = details.mediaSize.get(userId);
617         if (map != null) {
618             for (String key : keys) {
619                 if (map.containsKey(key)) {
620                     total += map.get(key);
621                 }
622             }
623         } else {
624             Log.w(TAG, "MeasurementDetails mediaSize array does not have key for user " + userId);
625         }
626         return total;
627     }
628 }
629