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.DownloadManager;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.res.Resources;
23 import android.graphics.drawable.ShapeDrawable;
24 import android.graphics.drawable.shapes.RectShape;
25 import android.os.Bundle;
26 import android.os.Environment;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.os.storage.StorageManager;
30 import android.os.storage.StorageVolume;
31 import android.preference.Preference;
32 import android.preference.PreferenceCategory;
33 import android.text.format.Formatter;
35 import com.android.settings.R;
36 import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
38 import java.util.HashSet;
41 public class StorageVolumePreferenceCategory extends PreferenceCategory implements
44 static final int TOTAL_SIZE = 0;
45 static final int APPLICATIONS = 1;
46 static final int DCIM = 2; // Pictures and Videos
47 static final int MUSIC = 3;
48 static final int DOWNLOADS = 4;
49 static final int MISC = 5;
50 static final int AVAILABLE = 6;
52 private UsageBarPreference mUsageBarPreference;
53 private Preference[] mPreferences;
54 private Preference mMountTogglePreference;
55 private Preference mFormatPreference;
56 private int[] mColors;
58 private Resources mResources;
60 private StorageVolume mStorageVolume;
62 private StorageManager mStorageManager = null;
64 private StorageMeasurement mMeasurement;
66 static class CategoryInfo {
70 public CategoryInfo(int title, int color) {
76 static final CategoryInfo[] sCategoryInfos = new CategoryInfo[] {
77 new CategoryInfo(R.string.memory_size, 0),
78 new CategoryInfo(R.string.memory_apps_usage, R.color.memory_apps_usage),
79 new CategoryInfo(R.string.memory_dcim_usage, R.color.memory_dcim),
80 new CategoryInfo(R.string.memory_music_usage, R.color.memory_music),
81 new CategoryInfo(R.string.memory_downloads_usage, R.color.memory_downloads),
82 new CategoryInfo(R.string.memory_media_misc_usage, R.color.memory_misc),
83 new CategoryInfo(R.string.memory_available, R.color.memory_avail),
86 public static final Set<String> sPathsExcludedForMisc = new HashSet<String>();
88 static class MediaCategory {
89 final String[] mDirPaths;
91 //final int mMediaType;
93 public MediaCategory(int category, String... directories) {
95 final int length = directories.length;
96 mDirPaths = new String[length];
97 for (int i = 0; i < length; i++) {
98 final String name = directories[i];
99 final String path = Environment.getExternalStoragePublicDirectory(name).
102 sPathsExcludedForMisc.add(path);
107 static final MediaCategory[] sMediaCategories = new MediaCategory[] {
108 new MediaCategory(DCIM, Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES,
109 Environment.DIRECTORY_PICTURES),
110 new MediaCategory(MUSIC, Environment.DIRECTORY_MUSIC, Environment.DIRECTORY_ALARMS,
111 Environment.DIRECTORY_NOTIFICATIONS, Environment.DIRECTORY_RINGTONES,
112 Environment.DIRECTORY_PODCASTS)
117 sPathsExcludedForMisc.add(Environment.getExternalStoragePublicDirectory(
118 Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());
120 sPathsExcludedForMisc.add(Environment.getExternalStorageDirectory().getAbsolutePath() +
124 // Updates the memory usage bar graph.
125 private static final int MSG_UI_UPDATE_APPROXIMATE = 1;
127 // Updates the memory usage bar graph.
128 private static final int MSG_UI_UPDATE_EXACT = 2;
130 private Handler mUpdateHandler = new Handler() {
132 public void handleMessage(Message msg) {
134 case MSG_UI_UPDATE_APPROXIMATE: {
135 Bundle bundle = msg.getData();
136 final long totalSize = bundle.getLong(StorageMeasurement.TOTAL_SIZE);
137 final long availSize = bundle.getLong(StorageMeasurement.AVAIL_SIZE);
138 updateApproximate(totalSize, availSize);
141 case MSG_UI_UPDATE_EXACT: {
142 Bundle bundle = msg.getData();
143 final long totalSize = bundle.getLong(StorageMeasurement.TOTAL_SIZE);
144 final long availSize = bundle.getLong(StorageMeasurement.AVAIL_SIZE);
145 final long appsUsed = bundle.getLong(StorageMeasurement.APPS_USED);
146 final long downloadsSize = bundle.getLong(StorageMeasurement.DOWNLOADS_SIZE);
147 final long miscSize = bundle.getLong(StorageMeasurement.MISC_SIZE);
148 final long[] mediaSizes = bundle.getLongArray(StorageMeasurement.MEDIA_SIZES);
149 updateExact(totalSize, availSize, appsUsed, downloadsSize, miscSize,
157 public StorageVolumePreferenceCategory(Context context, Resources resources,
158 StorageVolume storageVolume, StorageManager storageManager, boolean isPrimary) {
160 mResources = resources;
161 mStorageVolume = storageVolume;
162 mStorageManager = storageManager;
163 setTitle(storageVolume.getDescription());
164 mMeasurement = StorageMeasurement.getInstance(context, storageVolume, isPrimary);
165 mMeasurement.setReceiver(this);
169 mUsageBarPreference = new UsageBarPreference(getContext());
171 final int width = (int) mResources.getDimension(R.dimen.device_memory_usage_button_width);
172 final int height = (int) mResources.getDimension(R.dimen.device_memory_usage_button_height);
174 final int numberOfCategories = sCategoryInfos.length;
175 mPreferences = new Preference[numberOfCategories];
176 mColors = new int[numberOfCategories];
177 for (int i = 0; i < numberOfCategories; i++) {
178 final Preference preference = new Preference(getContext());
179 mPreferences[i] = preference;
180 preference.setTitle(sCategoryInfos[i].mTitle);
181 preference.setSummary(R.string.memory_calculating_size);
182 if (i != TOTAL_SIZE) {
183 // TOTAL_SIZE has no associated color
184 mColors[i] = mResources.getColor(sCategoryInfos[i].mColor);
185 preference.setIcon(createRectShape(width, height, mColors[i]));
189 mMountTogglePreference = new Preference(getContext());
190 mMountTogglePreference.setTitle(R.string.sd_eject);
191 mMountTogglePreference.setSummary(R.string.sd_eject_summary);
193 mFormatPreference = new Preference(getContext());
194 mFormatPreference.setTitle(R.string.sd_format);
195 mFormatPreference.setSummary(R.string.sd_format_summary);
198 public StorageVolume getStorageVolume() {
199 return mStorageVolume;
203 * Successive mounts can change the list of visible preferences.
204 * This makes sure all preferences are visible and displayed in the right order.
206 private void resetPreferences() {
207 final int numberOfCategories = sCategoryInfos.length;
209 removePreference(mUsageBarPreference);
210 for (int i = 0; i < numberOfCategories; i++) {
211 removePreference(mPreferences[i]);
213 removePreference(mMountTogglePreference);
214 removePreference(mFormatPreference);
216 addPreference(mUsageBarPreference);
217 for (int i = 0; i < numberOfCategories; i++) {
218 addPreference(mPreferences[i]);
220 addPreference(mMountTogglePreference);
221 addPreference(mFormatPreference);
223 mMountTogglePreference.setEnabled(true);
226 private void updatePreferencesFromState() {
229 String state = mStorageManager.getVolumeState(mStorageVolume.getPath());
231 String readOnly = "";
232 if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
233 state = Environment.MEDIA_MOUNTED;
234 readOnly = mResources.getString(R.string.read_only);
235 removePreference(mFormatPreference);
238 if (mStorageVolume.isEmulated()) {
239 removePreference(mFormatPreference);
242 if (!mStorageVolume.isRemovable() && !Environment.MEDIA_UNMOUNTED.equals(state)) {
243 // This device has built-in storage that is not removable.
244 // There is no reason for the user to unmount it.
245 removePreference(mMountTogglePreference);
248 if (Environment.MEDIA_MOUNTED.equals(state)) {
249 mPreferences[AVAILABLE].setSummary(mPreferences[AVAILABLE].getSummary() + readOnly);
251 mMountTogglePreference.setEnabled(true);
252 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
253 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary));
255 if (Environment.MEDIA_UNMOUNTED.equals(state) || Environment.MEDIA_NOFS.equals(state)
256 || Environment.MEDIA_UNMOUNTABLE.equals(state)) {
257 mMountTogglePreference.setEnabled(true);
258 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
259 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_mount_summary));
261 mMountTogglePreference.setEnabled(false);
262 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
263 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_insert_summary));
266 removePreference(mUsageBarPreference);
267 removePreference(mPreferences[TOTAL_SIZE]);
268 removePreference(mPreferences[AVAILABLE]);
269 removePreference(mFormatPreference);
273 public void updateApproximate(long totalSize, long availSize) {
274 mPreferences[TOTAL_SIZE].setSummary(formatSize(totalSize));
275 mPreferences[AVAILABLE].setSummary(formatSize(availSize));
277 final long usedSize = totalSize - availSize;
279 mUsageBarPreference.clear();
280 mUsageBarPreference.addEntry(usedSize / (float) totalSize, android.graphics.Color.GRAY);
281 mUsageBarPreference.commit();
283 updatePreferencesFromState();
286 public void updateExact(long totalSize, long availSize, long appsSize, long downloadsSize,
287 long miscSize, long[] mediaSizes) {
288 mUsageBarPreference.clear();
290 mPreferences[TOTAL_SIZE].setSummary(formatSize(totalSize));
292 if (!mMeasurement.isPrimary()) {
293 // TODO FIXME: external SD card will not report any size. Show used space in bar graph
294 final long usedSize = totalSize - availSize;
295 mUsageBarPreference.addEntry(usedSize / (float) totalSize, android.graphics.Color.GRAY);
298 updatePreference(appsSize, totalSize, APPLICATIONS);
300 long totalMediaSize = 0;
301 for (int i = 0; i < sMediaCategories.length; i++) {
302 final int category = sMediaCategories[i].mCategory;
303 final long size = mediaSizes[i];
304 updatePreference(size, totalSize, category);
305 totalMediaSize += size;
308 updatePreference(downloadsSize, totalSize, DOWNLOADS);
310 // Note miscSize != totalSize - availSize - appsSize - downloadsSize - totalMediaSize
311 // Block size is taken into account. That can be extra space from folders. TODO Investigate
312 updatePreference(miscSize, totalSize, MISC);
314 updatePreference(availSize, totalSize, AVAILABLE);
316 mUsageBarPreference.commit();
319 private void updatePreference(long size, long totalSize, int category) {
321 mPreferences[category].setSummary(formatSize(size));
322 mUsageBarPreference.addEntry(size / (float) totalSize, mColors[category]);
324 removePreference(mPreferences[category]);
328 private void measure() {
329 mMeasurement.invalidate();
330 mMeasurement.measure();
333 public void onResume() {
334 mMeasurement.setReceiver(this);
338 public void onStorageStateChanged() {
342 public void onMediaScannerFinished() {
346 public void onPause() {
347 mMeasurement.cleanUp();
350 private static ShapeDrawable createRectShape(int width, int height, int color) {
351 ShapeDrawable shape = new ShapeDrawable(new RectShape());
352 shape.setIntrinsicHeight(height);
353 shape.setIntrinsicWidth(width);
354 shape.getPaint().setColor(color);
358 private String formatSize(long size) {
359 return Formatter.formatFileSize(getContext(), size);
363 public void updateApproximate(Bundle bundle) {
364 final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE);
365 message.setData(bundle);
366 mUpdateHandler.sendMessage(message);
370 public void updateExact(Bundle bundle) {
371 final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXACT);
372 message.setData(bundle);
373 mUpdateHandler.sendMessage(message);
376 public boolean mountToggleClicked(Preference preference) {
377 return preference == mMountTogglePreference;
380 public Intent intentForClick(Preference preference) {
381 Intent intent = null;
383 // TODO The current "delete" story is not fully handled by the respective applications.
384 // When it is done, make sure the intent types below are correct.
385 // If that cannot be done, remove these intents.
386 if (preference == mFormatPreference) {
387 intent = new Intent(Intent.ACTION_VIEW);
388 intent.setClass(getContext(), com.android.settings.MediaFormat.class);
389 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mStorageVolume);
390 } else if (preference == mPreferences[APPLICATIONS]) {
391 intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
392 intent.setClass(getContext(),
393 com.android.settings.Settings.ManageApplicationsActivity.class);
394 } else if (preference == mPreferences[DOWNLOADS]) {
395 intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra(
396 DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
397 } else if (preference == mPreferences[MUSIC]) {
398 intent = new Intent(Intent.ACTION_GET_CONTENT);
399 intent.setType("audio/mp3");
400 } else if (preference == mPreferences[DCIM]) {
401 intent = new Intent(Intent.ACTION_GET_CONTENT);
402 intent.setType("image/jpeg"); // TODO Create a Videos category, type = video/*
403 } else if (preference == mPreferences[MISC]) {
404 Context context = getContext().getApplicationContext();
405 if (mMeasurement.getMiscSize() > 0) {
406 intent = new Intent(context, MiscFilesHandler.class);
407 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mStorageVolume);