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));
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         setPrivateStorageCategoryPreferencesVisibility(true);
479     }
480 
getSizeInfo( SparseArray<StorageAsyncLoader.StorageResult> result, int userId)481     private StorageCacheHelper.StorageCache getSizeInfo(
482             SparseArray<StorageAsyncLoader.StorageResult> result, int userId) {
483         if (result == null) {
484             return mStorageCacheHelper.retrieveCachedSize();
485         }
486         StorageAsyncLoader.StorageResult data = result.get(userId);
487         StorageCacheHelper.StorageCache storageCache = new StorageCacheHelper.StorageCache();
488         storageCache.imagesSize = data.imagesSize;
489         storageCache.videosSize = data.videosSize;
490         storageCache.audioSize = data.audioSize;
491         storageCache.allAppsExceptGamesSize = data.allAppsExceptGamesSize;
492         storageCache.gamesSize = data.gamesSize;
493         storageCache.documentsSize = data.documentsSize;
494         storageCache.otherSize = data.otherSize;
495         storageCache.trashSize = data.trashSize;
496         storageCache.systemSize = data.systemSize;
497         // Everything else that hasn't already been attributed is tracked as
498         // belonging to system.
499         long attributedSize = 0;
500         for (int i = 0; i < result.size(); i++) {
501             final StorageAsyncLoader.StorageResult otherData = result.valueAt(i);
502             attributedSize +=
503                     otherData.gamesSize
504                             + otherData.audioSize
505                             + otherData.videosSize
506                             + otherData.imagesSize
507                             + otherData.documentsSize
508                             + otherData.otherSize
509                             + otherData.trashSize
510                             + otherData.allAppsExceptGamesSize;
511             attributedSize -= otherData.duplicateCodeSize;
512         }
513         // System size is equal for each user and should be added only once
514         attributedSize += data.systemSize;
515         storageCache.temporaryFilesSize = Math.max(DataUnit.GIBIBYTES.toBytes(1),
516                 mUsedBytes - attributedSize);
517         return storageCache;
518     }
519 
setUsedSize(long usedSizeBytes)520     public void setUsedSize(long usedSizeBytes) {
521         mUsedBytes = usedSizeBytes;
522     }
523 
setTotalSize(long totalSizeBytes)524     public void setTotalSize(long totalSizeBytes) {
525         mTotalSize = totalSizeBytes;
526     }
527 
launchPublicStorageIntent()528     private void launchPublicStorageIntent() {
529         final Intent intent = mVolume.buildBrowseIntent();
530         if (intent == null) {
531             return;
532         }
533         mContext.startActivityAsUser(intent, new UserHandle(mUserId));
534     }
535 
launchActivityWithUri(Uri dataUri)536     private void launchActivityWithUri(Uri dataUri) {
537         final Intent intent = new Intent(Intent.ACTION_VIEW);
538         intent.setData(dataUri);
539         mContext.startActivityAsUser(intent, new UserHandle(mUserId));
540     }
541 
launchAppsIntent()542     private void launchAppsIntent() {
543         final Bundle args = getWorkAnnotatedBundle(3);
544         args.putString(ManageApplications.EXTRA_CLASSNAME,
545                 Settings.StorageUseActivity.class.getName());
546         args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
547         args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription());
548         final Intent intent = new SubSettingLauncher(mContext)
549                 .setDestination(ManageApplications.class.getName())
550                 .setTitleRes(R.string.apps_storage)
551                 .setArguments(args)
552                 .setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
553                 .toIntent();
554         intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
555         Utils.launchIntent(mFragment, intent);
556     }
557 
launchGamesIntent()558     private void launchGamesIntent() {
559         final Bundle args = getWorkAnnotatedBundle(1);
560         args.putString(ManageApplications.EXTRA_CLASSNAME,
561                 Settings.GamesStorageActivity.class.getName());
562         final Intent intent = new SubSettingLauncher(mContext)
563                 .setDestination(ManageApplications.class.getName())
564                 .setTitleRes(R.string.game_storage_settings)
565                 .setArguments(args)
566                 .setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
567                 .toIntent();
568         intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
569         Utils.launchIntent(mFragment, intent);
570     }
571 
getWorkAnnotatedBundle(int additionalCapacity)572     private Bundle getWorkAnnotatedBundle(int additionalCapacity) {
573         final Bundle args = new Bundle(1 + additionalCapacity);
574         if (mProfileType == ProfileSelectFragment.ProfileType.WORK) {
575             args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, WORK_TAB);
576         } else if (mProfileType == ProfileSelectFragment.ProfileType.PRIVATE) {
577             args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, PRIVATE_TAB);
578         } else {
579             args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, PERSONAL_TAB);
580         }
581         return args;
582     }
583 
launchTrashIntent()584     private void launchTrashIntent() {
585         final Intent intent = new Intent("android.settings.VIEW_TRASH");
586 
587         if (mPackageManager.resolveActivityAsUser(intent, 0 /* flags */, mUserId) == null) {
588             final long trashSize = mTrashPreference.getStorageSize();
589             if (trashSize > 0) {
590                 new EmptyTrashFragment(mFragment, mUserId, trashSize,
591                         this /* onEmptyTrashCompleteListener */).show();
592             } else {
593                 Toast.makeText(mContext, R.string.storage_trash_dialog_empty_message,
594                         Toast.LENGTH_SHORT).show();
595             }
596         } else {
597             mContext.startActivityAsUser(intent, new UserHandle(mUserId));
598         }
599     }
600 
601     @Override
onEmptyTrashComplete()602     public void onEmptyTrashComplete() {
603         if (mTrashPreference == null) {
604             return;
605         }
606         mTrashPreference.setStorageSize(0, mTotalSize, true /* animate */);
607         updatePrivateStorageCategoryPreferencesOrder();
608     }
609 
totalValues(StorageMeasurement.MeasurementDetails details, int userId, String... keys)610     private static long totalValues(StorageMeasurement.MeasurementDetails details, int userId,
611             String... keys) {
612         long total = 0;
613         Map<String, Long> map = details.mediaSize.get(userId);
614         if (map != null) {
615             for (String key : keys) {
616                 if (map.containsKey(key)) {
617                     total += map.get(key);
618                 }
619             }
620         } else {
621             Log.w(TAG, "MeasurementDetails mediaSize array does not have key for user " + userId);
622         }
623         return total;
624     }
625 }
626