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