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.tv.settings.device.storage; 18 19 import android.app.ActivityManager; 20 import android.content.pm.PackageManager; 21 import android.os.Bundle; 22 import android.os.Environment; 23 import android.os.storage.StorageManager; 24 import android.os.storage.VolumeInfo; 25 import android.text.TextUtils; 26 import android.util.Log; 27 28 import androidx.preference.Preference; 29 import androidx.preference.PreferenceScreen; 30 31 import com.android.internal.logging.nano.MetricsProto; 32 import com.android.settingslib.deviceinfo.StorageMeasurement; 33 import com.android.tv.settings.R; 34 import com.android.tv.settings.SettingsPreferenceFragment; 35 import com.android.tv.settings.device.apps.AppsFragment; 36 37 import java.util.HashMap; 38 import java.util.List; 39 40 /** 41 * The screen in TV settings that lets users manage external storage devices and that shows the 42 * usage summary by data type such as apps, music, download and so on. 43 */ 44 public class StorageFragment extends SettingsPreferenceFragment { 45 private static final String TAG = "StorageFragment"; 46 47 private static final String KEY_MIGRATE = "migrate"; 48 private static final String KEY_EJECT = "eject"; 49 private static final String KEY_ERASE = "erase"; 50 private static final String KEY_APPS_USAGE = "apps_usage"; 51 private static final String KEY_DCIM_USAGE = "dcim_usage"; 52 private static final String KEY_MUSIC_USAGE = "music_usage"; 53 private static final String KEY_DOWNLOADS_USAGE = "downloads_usage"; 54 private static final String KEY_CACHE_USAGE = "cache_usage"; 55 private static final String KEY_MISC_USAGE = "misc_usage"; 56 private static final String KEY_AVAILABLE = "available"; 57 58 private StorageManager mStorageManager; 59 private PackageManager mPackageManager; 60 61 private VolumeInfo mVolumeInfo; 62 63 private StorageMeasurement mMeasure; 64 private final StorageMeasurement.MeasurementReceiver mMeasurementReceiver = 65 new MeasurementReceiver(); 66 private final StorageEventListener mStorageEventListener = new StorageEventListener(); 67 68 private Preference mMigratePref; 69 private Preference mEjectPref; 70 private Preference mErasePref; 71 private StoragePreference mAppsUsagePref; 72 private StoragePreference mDcimUsagePref; 73 private StoragePreference mMusicUsagePref; 74 private StoragePreference mDownloadsUsagePref; 75 private StoragePreference mCacheUsagePref; 76 private StoragePreference mMiscUsagePref; 77 private StoragePreference mAvailablePref; 78 prepareArgs(Bundle bundle, VolumeInfo volumeInfo)79 public static void prepareArgs(Bundle bundle, VolumeInfo volumeInfo) { 80 bundle.putString(VolumeInfo.EXTRA_VOLUME_ID, volumeInfo.getId()); 81 } 82 83 @Override onCreate(Bundle savedInstanceState)84 public void onCreate(Bundle savedInstanceState) { 85 mStorageManager = getContext().getSystemService(StorageManager.class); 86 mPackageManager = getContext().getPackageManager(); 87 88 mVolumeInfo = mStorageManager.findVolumeById( 89 getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID)); 90 91 super.onCreate(savedInstanceState); 92 } 93 94 @Override onStart()95 public void onStart() { 96 super.onStart(); 97 mStorageManager.registerListener(mStorageEventListener); 98 startMeasurement(); 99 } 100 101 @Override onResume()102 public void onResume() { 103 super.onResume(); 104 mVolumeInfo = mStorageManager.findVolumeById( 105 getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID)); 106 if (mVolumeInfo == null || !mVolumeInfo.isMountedReadable()) { 107 getFragmentManager().popBackStack(); 108 } else { 109 refresh(); 110 } 111 } 112 113 @Override onStop()114 public void onStop() { 115 super.onStop(); 116 mStorageManager.unregisterListener(mStorageEventListener); 117 stopMeasurement(); 118 } 119 120 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)121 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 122 setPreferencesFromResource(R.xml.storage, null); 123 124 final PreferenceScreen screen = getPreferenceScreen(); 125 screen.setTitle(mStorageManager.getBestVolumeDescription(mVolumeInfo)); 126 127 mMigratePref = findPreference(KEY_MIGRATE); 128 mEjectPref = findPreference(KEY_EJECT); 129 mErasePref = findPreference(KEY_ERASE); 130 131 mAppsUsagePref = (StoragePreference) findPreference(KEY_APPS_USAGE); 132 mDcimUsagePref = (StoragePreference) findPreference(KEY_DCIM_USAGE); 133 mMusicUsagePref = (StoragePreference) findPreference(KEY_MUSIC_USAGE); 134 mDownloadsUsagePref = (StoragePreference) findPreference(KEY_DOWNLOADS_USAGE); 135 mCacheUsagePref = (StoragePreference) findPreference(KEY_CACHE_USAGE); 136 mMiscUsagePref = (StoragePreference) findPreference(KEY_MISC_USAGE); 137 mAvailablePref = (StoragePreference) findPreference(KEY_AVAILABLE); 138 } 139 refresh()140 private void refresh() { 141 boolean showMigrate = false; 142 final VolumeInfo currentExternal = mPackageManager.getPrimaryStorageCurrentVolume(); 143 // currentExternal will be null if the drive is not mounted. Don't offer the option to 144 // migrate if so. 145 if (currentExternal != null 146 && !TextUtils.equals(currentExternal.getId(), mVolumeInfo.getId())) { 147 final List<VolumeInfo> candidates = 148 mPackageManager.getPrimaryStorageCandidateVolumes(); 149 for (final VolumeInfo candidate : candidates) { 150 if (TextUtils.equals(candidate.getId(), mVolumeInfo.getId())) { 151 showMigrate = true; 152 break; 153 } 154 } 155 } 156 157 mMigratePref.setVisible(showMigrate); 158 mMigratePref.setIntent( 159 MigrateStorageActivity.getLaunchIntent(getContext(), mVolumeInfo.getId(), true)); 160 161 final String description = mStorageManager.getBestVolumeDescription(mVolumeInfo); 162 163 final boolean privateInternal = VolumeInfo.ID_PRIVATE_INTERNAL.equals(mVolumeInfo.getId()); 164 final boolean isPrivate = mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE; 165 166 mEjectPref.setVisible(!privateInternal); 167 mEjectPref.setIntent(UnmountActivity.getIntent(getContext(), mVolumeInfo.getId(), 168 description)); 169 mErasePref.setVisible(!privateInternal); 170 if (isPrivate) { 171 mErasePref.setIntent( 172 FormatActivity.getFormatAsPublicIntent(getContext(), mVolumeInfo.getDiskId())); 173 mErasePref.setTitle(R.string.storage_format_as_public); 174 } else { 175 mErasePref.setIntent( 176 FormatActivity.getFormatAsPrivateIntent(getContext(), mVolumeInfo.getDiskId())); 177 mErasePref.setTitle(R.string.storage_format_as_private); 178 } 179 180 mAppsUsagePref.setVisible(isPrivate); 181 mAppsUsagePref.setFragment(AppsFragment.class.getName()); 182 AppsFragment.prepareArgs(mAppsUsagePref.getExtras(), mVolumeInfo.fsUuid, description); 183 mDcimUsagePref.setVisible(isPrivate); 184 mMusicUsagePref.setVisible(isPrivate); 185 mDownloadsUsagePref.setVisible(isPrivate); 186 mCacheUsagePref.setVisible(isPrivate); 187 mCacheUsagePref.setFragment(ConfirmClearCacheFragment.class.getName()); 188 } 189 startMeasurement()190 private void startMeasurement() { 191 if (mVolumeInfo != null && mVolumeInfo.isMountedReadable()) { 192 final VolumeInfo sharedVolume = mStorageManager.findEmulatedForPrivate(mVolumeInfo); 193 mMeasure = new StorageMeasurement(getContext(), mVolumeInfo, sharedVolume); 194 mMeasure.setReceiver(mMeasurementReceiver); 195 mMeasure.forceMeasure(); 196 } 197 } 198 stopMeasurement()199 private void stopMeasurement() { 200 if (mMeasure != null) { 201 mMeasure.onDestroy(); 202 } 203 } 204 updateDetails(StorageMeasurement.MeasurementDetails details)205 private void updateDetails(StorageMeasurement.MeasurementDetails details) { 206 final int currentUser = ActivityManager.getCurrentUser(); 207 final long dcimSize = totalValues(details.mediaSize.get(currentUser), 208 Environment.DIRECTORY_DCIM, 209 Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES); 210 211 final long musicSize = totalValues(details.mediaSize.get(currentUser), 212 Environment.DIRECTORY_MUSIC, 213 Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS, 214 Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS); 215 216 final long downloadsSize = totalValues(details.mediaSize.get(currentUser), 217 Environment.DIRECTORY_DOWNLOADS); 218 219 mAvailablePref.setSize(details.availSize); 220 mAppsUsagePref.setSize(details.appsSize.get(currentUser)); 221 mDcimUsagePref.setSize(dcimSize); 222 mMusicUsagePref.setSize(musicSize); 223 mDownloadsUsagePref.setSize(downloadsSize); 224 mCacheUsagePref.setSize(details.cacheSize); 225 mMiscUsagePref.setSize(details.miscSize.get(currentUser)); 226 } 227 totalValues(HashMap<String, Long> map, String... keys)228 private static long totalValues(HashMap<String, Long> map, String... keys) { 229 long total = 0; 230 if (map != null) { 231 for (String key : keys) { 232 if (map.containsKey(key)) { 233 total += map.get(key); 234 } 235 } 236 } else { 237 Log.w(TAG, 238 "MeasurementDetails mediaSize array does not have key for current user " + 239 ActivityManager.getCurrentUser()); 240 } 241 return total; 242 } 243 244 private class MeasurementReceiver implements StorageMeasurement.MeasurementReceiver { 245 246 @Override onDetailsChanged(StorageMeasurement.MeasurementDetails details)247 public void onDetailsChanged(StorageMeasurement.MeasurementDetails details) { 248 updateDetails(details); 249 } 250 } 251 252 private class StorageEventListener extends android.os.storage.StorageEventListener { 253 @Override onVolumeStateChanged(VolumeInfo vol, int oldState, int newState)254 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 255 mVolumeInfo = vol; 256 if (isResumed()) { 257 if (mVolumeInfo.isMountedReadable()) { 258 refresh(); 259 } else { 260 getFragmentManager().popBackStack(); 261 } 262 } 263 } 264 } 265 266 @Override getMetricsCategory()267 public int getMetricsCategory() { 268 return MetricsProto.MetricsEvent.SETTINGS_STORAGE_CATEGORY; 269 } 270 } 271