2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.deviceinfo;
19 import android.app.ActivityManagerNative;
20 import android.app.ActivityThread;
21 import android.app.DownloadManager;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.IPackageManager;
25 import android.content.pm.UserInfo;
26 import android.content.res.Resources;
27 import android.hardware.usb.UsbManager;
28 import android.os.Environment;
29 import android.os.Handler;
30 import android.os.Message;
31 import android.os.RemoteException;
32 import android.os.UserManager;
33 import android.os.storage.StorageManager;
34 import android.os.storage.StorageVolume;
35 import android.preference.Preference;
36 import android.preference.PreferenceCategory;
37 import android.provider.MediaStore;
38 import android.text.format.Formatter;
40 import com.android.settings.R;
41 import com.android.settings.deviceinfo.StorageMeasurement.MeasurementDetails;
42 import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
43 import com.google.android.collect.Lists;
45 import java.util.HashMap;
46 import java.util.Iterator;
47 import java.util.List;
49 public class StorageVolumePreferenceCategory extends PreferenceCategory {
50 public static final String KEY_CACHE = "cache";
52 private static final int ORDER_USAGE_BAR = -2;
53 private static final int ORDER_STORAGE_LOW = -1;
55 /** Physical volume being measured, or {@code null} for internal. */
56 private final StorageVolume mVolume;
57 private final StorageMeasurement mMeasure;
59 private final Resources mResources;
60 private final StorageManager mStorageManager;
61 private final UserManager mUserManager;
63 private UsageBarPreference mUsageBarPreference;
64 private Preference mMountTogglePreference;
65 private Preference mFormatPreference;
66 private Preference mStorageLow;
68 private StorageItemPreference mItemTotal;
69 private StorageItemPreference mItemAvailable;
70 private StorageItemPreference mItemApps;
71 private StorageItemPreference mItemDcim;
72 private StorageItemPreference mItemMusic;
73 private StorageItemPreference mItemDownloads;
74 private StorageItemPreference mItemCache;
75 private StorageItemPreference mItemMisc;
76 private List<StorageItemPreference> mItemUsers = Lists.newArrayList();
78 private boolean mUsbConnected;
79 private String mUsbFunction;
81 private long mTotalSize;
83 private static final int MSG_UI_UPDATE_APPROXIMATE = 1;
84 private static final int MSG_UI_UPDATE_DETAILS = 2;
86 private Handler mUpdateHandler = new Handler() {
88 public void handleMessage(Message msg) {
90 case MSG_UI_UPDATE_APPROXIMATE: {
91 final long[] size = (long[]) msg.obj;
92 updateApproximate(size[0], size[1]);
95 case MSG_UI_UPDATE_DETAILS: {
96 final MeasurementDetails details = (MeasurementDetails) msg.obj;
97 updateDetails(details);
105 * Build category to summarize internal storage, including any emulated
106 * {@link StorageVolume}.
108 public static StorageVolumePreferenceCategory buildForInternal(Context context) {
109 return new StorageVolumePreferenceCategory(context, null);
113 * Build category to summarize specific physical {@link StorageVolume}.
115 public static StorageVolumePreferenceCategory buildForPhysical(
116 Context context, StorageVolume volume) {
117 return new StorageVolumePreferenceCategory(context, volume);
120 private StorageVolumePreferenceCategory(Context context, StorageVolume volume) {
124 mMeasure = StorageMeasurement.getInstance(context, volume);
126 mResources = context.getResources();
127 mStorageManager = StorageManager.from(context);
128 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
130 setTitle(volume != null ? volume.getDescription(context)
131 : context.getText(R.string.internal_storage));
134 private StorageItemPreference buildItem(int titleRes, int colorRes) {
135 return new StorageItemPreference(getContext(), titleRes, colorRes);
139 final Context context = getContext();
143 final UserInfo currentUser;
145 currentUser = ActivityManagerNative.getDefault().getCurrentUser();
146 } catch (RemoteException e) {
147 throw new RuntimeException("Failed to get current user");
150 final List<UserInfo> otherUsers = getUsersExcluding(currentUser);
151 final boolean showUsers = mVolume == null && otherUsers.size() > 0;
153 mUsageBarPreference = new UsageBarPreference(context);
154 mUsageBarPreference.setOrder(ORDER_USAGE_BAR);
155 addPreference(mUsageBarPreference);
157 mItemTotal = buildItem(R.string.memory_size, 0);
158 mItemAvailable = buildItem(R.string.memory_available, R.color.memory_avail);
159 addPreference(mItemTotal);
160 addPreference(mItemAvailable);
162 mItemApps = buildItem(R.string.memory_apps_usage, R.color.memory_apps_usage);
163 mItemDcim = buildItem(R.string.memory_dcim_usage, R.color.memory_dcim);
164 mItemMusic = buildItem(R.string.memory_music_usage, R.color.memory_music);
165 mItemDownloads = buildItem(R.string.memory_downloads_usage, R.color.memory_downloads);
166 mItemCache = buildItem(R.string.memory_media_cache_usage, R.color.memory_cache);
167 mItemMisc = buildItem(R.string.memory_media_misc_usage, R.color.memory_misc);
169 mItemCache.setKey(KEY_CACHE);
171 final boolean showDetails = mVolume == null || mVolume.isPrimary();
174 addPreference(new PreferenceHeader(context, currentUser.name));
177 addPreference(mItemApps);
178 addPreference(mItemDcim);
179 addPreference(mItemMusic);
180 addPreference(mItemDownloads);
181 addPreference(mItemCache);
182 addPreference(mItemMisc);
185 addPreference(new PreferenceHeader(context, R.string.storage_other_users));
188 for (UserInfo info : otherUsers) {
189 final int colorRes = count++ % 2 == 0 ? R.color.memory_user_light
190 : R.color.memory_user_dark;
191 final StorageItemPreference userPref = new StorageItemPreference(
192 getContext(), info.name, colorRes, info.id);
193 mItemUsers.add(userPref);
194 addPreference(userPref);
199 final boolean isRemovable = mVolume != null ? mVolume.isRemovable() : false;
200 // Always create the preference since many code rely on it existing
201 mMountTogglePreference = new Preference(context);
203 mMountTogglePreference.setTitle(R.string.sd_eject);
204 mMountTogglePreference.setSummary(R.string.sd_eject_summary);
205 addPreference(mMountTogglePreference);
208 final boolean allowFormat = mVolume != null;
210 mFormatPreference = new Preference(context);
211 mFormatPreference.setTitle(R.string.sd_format);
212 mFormatPreference.setSummary(R.string.sd_format_summary);
213 addPreference(mFormatPreference);
216 final IPackageManager pm = ActivityThread.getPackageManager();
218 if (pm.isStorageLow()) {
219 mStorageLow = new Preference(context);
220 mStorageLow.setOrder(ORDER_STORAGE_LOW);
221 mStorageLow.setTitle(R.string.storage_low_title);
222 mStorageLow.setSummary(R.string.storage_low_summary);
223 addPreference(mStorageLow);
224 } else if (mStorageLow != null) {
225 removePreference(mStorageLow);
228 } catch (RemoteException e) {
232 public StorageVolume getStorageVolume() {
236 private void updatePreferencesFromState() {
237 // Only update for physical volumes
238 if (mVolume == null) return;
240 mMountTogglePreference.setEnabled(true);
242 final String state = mStorageManager.getVolumeState(mVolume.getPath());
244 if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
245 mItemAvailable.setTitle(R.string.memory_available_read_only);
247 mItemAvailable.setTitle(R.string.memory_available);
250 if (Environment.MEDIA_MOUNTED.equals(state)
251 || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
252 mMountTogglePreference.setEnabled(true);
253 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
254 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary));
256 if (Environment.MEDIA_UNMOUNTED.equals(state) || Environment.MEDIA_NOFS.equals(state)
257 || Environment.MEDIA_UNMOUNTABLE.equals(state)) {
258 mMountTogglePreference.setEnabled(true);
259 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
260 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_mount_summary));
262 mMountTogglePreference.setEnabled(false);
263 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
264 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_insert_summary));
267 removePreference(mUsageBarPreference);
268 removePreference(mItemTotal);
269 removePreference(mItemAvailable);
272 if (mUsbConnected && (UsbManager.USB_FUNCTION_MTP.equals(mUsbFunction) ||
273 UsbManager.USB_FUNCTION_PTP.equals(mUsbFunction))) {
274 mMountTogglePreference.setEnabled(false);
275 if (Environment.MEDIA_MOUNTED.equals(state)
276 || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
277 mMountTogglePreference.setSummary(
278 mResources.getString(R.string.mtp_ptp_mode_summary));
281 if (mFormatPreference != null) {
282 mFormatPreference.setEnabled(false);
283 mFormatPreference.setSummary(mResources.getString(R.string.mtp_ptp_mode_summary));
285 } else if (mFormatPreference != null) {
286 mFormatPreference.setEnabled(true);
287 mFormatPreference.setSummary(mResources.getString(R.string.sd_format_summary));
291 public void updateApproximate(long totalSize, long availSize) {
292 mItemTotal.setSummary(formatSize(totalSize));
293 mItemAvailable.setSummary(formatSize(availSize));
295 mTotalSize = totalSize;
297 final long usedSize = totalSize - availSize;
299 mUsageBarPreference.clear();
300 mUsageBarPreference.addEntry(0, usedSize / (float) totalSize, android.graphics.Color.GRAY);
301 mUsageBarPreference.commit();
303 updatePreferencesFromState();
306 private static long totalValues(HashMap<String, Long> map, String... keys) {
308 for (String key : keys) {
309 if (map.containsKey(key)) {
310 total += map.get(key);
316 public void updateDetails(MeasurementDetails details) {
317 final boolean showDetails = mVolume == null || mVolume.isPrimary();
318 if (!showDetails) return;
320 // Count caches as available space, since system manages them
321 mItemTotal.setSummary(formatSize(details.totalSize));
322 mItemAvailable.setSummary(formatSize(details.availSize));
324 mUsageBarPreference.clear();
326 updatePreference(mItemApps, details.appsSize);
328 final long dcimSize = totalValues(details.mediaSize, Environment.DIRECTORY_DCIM,
329 Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES);
330 updatePreference(mItemDcim, dcimSize);
332 final long musicSize = totalValues(details.mediaSize, Environment.DIRECTORY_MUSIC,
333 Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
334 Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
335 updatePreference(mItemMusic, musicSize);
337 final long downloadsSize = totalValues(details.mediaSize, Environment.DIRECTORY_DOWNLOADS);
338 updatePreference(mItemDownloads, downloadsSize);
340 updatePreference(mItemCache, details.cacheSize);
341 updatePreference(mItemMisc, details.miscSize);
343 for (StorageItemPreference userPref : mItemUsers) {
344 final long userSize = details.usersSize.get(userPref.userHandle);
345 updatePreference(userPref, userSize);
348 mUsageBarPreference.commit();
351 private void updatePreference(StorageItemPreference pref, long size) {
353 pref.setSummary(formatSize(size));
354 final int order = pref.getOrder();
355 mUsageBarPreference.addEntry(order, size / (float) mTotalSize, pref.color);
357 removePreference(pref);
361 private void measure() {
362 mMeasure.invalidate();
366 public void onResume() {
367 mMeasure.setReceiver(mReceiver);
371 public void onStorageStateChanged() {
376 public void onUsbStateChanged(boolean isUsbConnected, String usbFunction) {
377 mUsbConnected = isUsbConnected;
378 mUsbFunction = usbFunction;
382 public void onMediaScannerFinished() {
386 public void onCacheCleared() {
390 public void onPause() {
394 private String formatSize(long size) {
395 return Formatter.formatFileSize(getContext(), size);
398 private MeasurementReceiver mReceiver = new MeasurementReceiver() {
400 public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize) {
401 mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE, new long[] {
402 totalSize, availSize }).sendToTarget();
406 public void updateDetails(StorageMeasurement meas, MeasurementDetails details) {
407 mUpdateHandler.obtainMessage(MSG_UI_UPDATE_DETAILS, details).sendToTarget();
411 public boolean mountToggleClicked(Preference preference) {
412 return preference == mMountTogglePreference;
415 public Intent intentForClick(Preference pref) {
416 Intent intent = null;
418 // TODO The current "delete" story is not fully handled by the respective applications.
419 // When it is done, make sure the intent types below are correct.
420 // If that cannot be done, remove these intents.
421 final String key = pref.getKey();
422 if (pref == mFormatPreference) {
423 intent = new Intent(Intent.ACTION_VIEW);
424 intent.setClass(getContext(), com.android.settings.MediaFormat.class);
425 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
426 } else if (pref == mItemApps) {
427 intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
428 intent.setClass(getContext(),
429 com.android.settings.Settings.ManageApplicationsActivity.class);
430 } else if (pref == mItemDownloads) {
431 intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra(
432 DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
433 } else if (pref == mItemMusic) {
434 intent = new Intent(Intent.ACTION_GET_CONTENT);
435 intent.setType("audio/mp3");
436 } else if (pref == mItemDcim) {
437 intent = new Intent(Intent.ACTION_VIEW);
438 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
439 // TODO Create a Videos category, MediaStore.Video.Media.EXTERNAL_CONTENT_URI
440 intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
441 } else if (pref == mItemMisc) {
442 Context context = getContext().getApplicationContext();
443 intent = new Intent(context, MiscFilesHandler.class);
444 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
450 public static class PreferenceHeader extends Preference {
451 public PreferenceHeader(Context context, int titleRes) {
452 super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
456 public PreferenceHeader(Context context, CharSequence title) {
457 super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
462 public boolean isEnabled() {
468 * Return list of other users, excluding the current user.
470 private List<UserInfo> getUsersExcluding(UserInfo excluding) {
471 final List<UserInfo> users = mUserManager.getUsers();
472 final Iterator<UserInfo> i = users.iterator();
473 while (i.hasNext()) {
474 if (i.next().id == excluding.id) {