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.ServiceManager;
30 import android.os.storage.IMountService;
31 import android.os.storage.StorageVolume;
32 import android.preference.Preference;
33 import android.preference.PreferenceCategory;
34 import android.text.format.Formatter;
36 import com.android.settings.R;
37 import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
39 import java.util.HashSet;
42 public class StorageVolumePreferenceCategory extends PreferenceCategory implements
45 static final int TOTAL_SIZE = 0;
46 static final int APPLICATIONS = 1;
47 static final int DCIM = 2; // Pictures and Videos
48 static final int MUSIC = 3;
49 static final int DOWNLOADS = 4;
50 static final int MISC = 5;
51 static final int AVAILABLE = 6;
53 private UsageBarPreference mUsageBarPreference;
54 private Preference[] mPreferences;
55 private Preference mMountTogglePreference;
56 private Preference mFormatPreference;
57 private int[] mColors;
59 private Resources mResources;
61 private StorageVolume mStorageVolume;
63 private StorageMeasurement mMeasurement;
65 static class CategoryInfo {
69 public CategoryInfo(int title, int color) {
75 static final CategoryInfo[] sCategoryInfos = new CategoryInfo[] {
76 new CategoryInfo(R.string.memory_size, 0),
77 new CategoryInfo(R.string.memory_apps_usage, R.color.memory_apps_usage),
78 new CategoryInfo(R.string.memory_dcim_usage, R.color.memory_dcim),
79 new CategoryInfo(R.string.memory_music_usage, R.color.memory_music),
80 new CategoryInfo(R.string.memory_downloads_usage, R.color.memory_downloads),
81 new CategoryInfo(R.string.memory_media_misc_usage, R.color.memory_misc),
82 new CategoryInfo(R.string.memory_available, R.color.memory_avail),
85 public static final Set<String> sPathsExcludedForMisc = new HashSet<String>();
87 static class MediaCategory {
88 final String[] mDirPaths;
90 //final int mMediaType;
92 public MediaCategory(int category, String... directories) {
94 final int length = directories.length;
95 mDirPaths = new String[length];
96 for (int i = 0; i < length; i++) {
97 final String name = directories[i];
98 final String path = Environment.getExternalStoragePublicDirectory(name).
101 sPathsExcludedForMisc.add(path);
106 static final MediaCategory[] sMediaCategories = new MediaCategory[] {
107 new MediaCategory(DCIM, Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES,
108 Environment.DIRECTORY_PICTURES),
109 new MediaCategory(MUSIC, Environment.DIRECTORY_MUSIC, Environment.DIRECTORY_ALARMS,
110 Environment.DIRECTORY_NOTIFICATIONS, Environment.DIRECTORY_RINGTONES,
111 Environment.DIRECTORY_PODCASTS)
116 sPathsExcludedForMisc.add(Environment.getExternalStoragePublicDirectory(
117 Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());
119 sPathsExcludedForMisc.add(Environment.getExternalStorageDirectory().getAbsolutePath() +
123 // Updates the memory usage bar graph.
124 private static final int MSG_UI_UPDATE_APPROXIMATE = 1;
126 // Updates the memory usage bar graph.
127 private static final int MSG_UI_UPDATE_EXACT = 2;
129 // Key for the extra StorageVolume bundle added to the Misc intent.
130 static final String STORAGE_VOLUME = "storage_volume";
132 private Handler mUpdateHandler = new Handler() {
134 public void handleMessage(Message msg) {
136 case MSG_UI_UPDATE_APPROXIMATE: {
137 Bundle bundle = msg.getData();
138 final long totalSize = bundle.getLong(StorageMeasurement.TOTAL_SIZE);
139 final long availSize = bundle.getLong(StorageMeasurement.AVAIL_SIZE);
140 updateApproximate(totalSize, availSize);
143 case MSG_UI_UPDATE_EXACT: {
144 Bundle bundle = msg.getData();
145 final long totalSize = bundle.getLong(StorageMeasurement.TOTAL_SIZE);
146 final long availSize = bundle.getLong(StorageMeasurement.AVAIL_SIZE);
147 final long appsUsed = bundle.getLong(StorageMeasurement.APPS_USED);
148 final long downloadsSize = bundle.getLong(StorageMeasurement.DOWNLOADS_SIZE);
149 final long miscSize = bundle.getLong(StorageMeasurement.MISC_SIZE);
150 final long[] mediaSizes = bundle.getLongArray(StorageMeasurement.MEDIA_SIZES);
151 updateExact(totalSize, availSize, appsUsed, downloadsSize, miscSize,
159 public StorageVolumePreferenceCategory(Context context, Resources resources,
160 StorageVolume storageVolume, boolean isPrimary) {
162 mResources = resources;
163 mStorageVolume = storageVolume;
164 setTitle(storageVolume.getDescription());
165 mMeasurement = StorageMeasurement.getInstance(context, storageVolume, isPrimary);
166 mMeasurement.setReceiver(this);
170 mUsageBarPreference = new UsageBarPreference(getContext());
172 final int width = (int) mResources.getDimension(R.dimen.device_memory_usage_button_width);
173 final int height = (int) mResources.getDimension(R.dimen.device_memory_usage_button_height);
175 final int numberOfCategories = sCategoryInfos.length;
176 mPreferences = new Preference[numberOfCategories];
177 mColors = new int[numberOfCategories];
178 for (int i = 0; i < numberOfCategories; i++) {
179 final Preference preference = new Preference(getContext());
180 mPreferences[i] = preference;
181 preference.setTitle(sCategoryInfos[i].mTitle);
182 preference.setSummary(R.string.memory_calculating_size);
183 if (i != TOTAL_SIZE) {
184 // TOTAL_SIZE has no associated color
185 mColors[i] = mResources.getColor(sCategoryInfos[i].mColor);
186 preference.setIcon(createRectShape(width, height, mColors[i]));
190 mMountTogglePreference = new Preference(getContext());
191 mMountTogglePreference.setTitle(R.string.sd_eject);
192 mMountTogglePreference.setSummary(R.string.sd_eject_summary);
194 mFormatPreference = new Preference(getContext());
195 mFormatPreference.setTitle(R.string.sd_format);
196 mFormatPreference.setSummary(R.string.sd_format_summary);
199 public String getMountPoint() {
200 return mStorageVolume.getPath();
203 public String getStorageVolumeState() {
205 IMountService mountService =
206 IMountService.Stub.asInterface(ServiceManager.getService("mount"));
207 return mountService.getVolumeState(getMountPoint());
208 } catch (Exception rex) {
209 return Environment.MEDIA_REMOVED;
214 * Successive mounts can change the list of visible preferences.
215 * This makes sure all preferences are visible and displayed in the right order.
217 private void resetPreferences() {
218 final int numberOfCategories = sCategoryInfos.length;
220 removePreference(mUsageBarPreference);
221 for (int i = 0; i < numberOfCategories; i++) {
222 removePreference(mPreferences[i]);
224 removePreference(mMountTogglePreference);
225 removePreference(mFormatPreference);
227 addPreference(mUsageBarPreference);
228 for (int i = 0; i < numberOfCategories; i++) {
229 addPreference(mPreferences[i]);
231 addPreference(mMountTogglePreference);
232 addPreference(mFormatPreference);
234 mMountTogglePreference.setEnabled(true);
237 private void updatePreferencesFromState() {
240 String state = getStorageVolumeState();
242 String readOnly = "";
243 if (state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
244 state = Environment.MEDIA_MOUNTED;
245 readOnly = mResources.getString(R.string.read_only);
246 removePreference(mFormatPreference);
249 if (mStorageVolume.isEmulated()) {
250 removePreference(mFormatPreference);
253 if (!mStorageVolume.isRemovable() && !state.equals(Environment.MEDIA_UNMOUNTED)) {
254 // This device has built-in storage that is not removable.
255 // There is no reason for the user to unmount it.
256 removePreference(mMountTogglePreference);
259 if (state.equals(Environment.MEDIA_MOUNTED)) {
260 mPreferences[AVAILABLE].setSummary(mPreferences[AVAILABLE].getSummary() + readOnly);
262 mMountTogglePreference.setEnabled(true);
263 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
264 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary));
266 if (state.equals(Environment.MEDIA_UNMOUNTED) || state.equals(Environment.MEDIA_NOFS)
267 || state.equals(Environment.MEDIA_UNMOUNTABLE)) {
268 mMountTogglePreference.setEnabled(true);
269 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
270 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_mount_summary));
272 mMountTogglePreference.setEnabled(false);
273 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
274 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_insert_summary));
277 removePreference(mUsageBarPreference);
278 removePreference(mPreferences[TOTAL_SIZE]);
279 removePreference(mPreferences[AVAILABLE]);
280 removePreference(mFormatPreference);
284 public void updateApproximate(long totalSize, long availSize) {
285 mPreferences[TOTAL_SIZE].setSummary(formatSize(totalSize));
286 mPreferences[AVAILABLE].setSummary(formatSize(availSize));
288 final long usedSize = totalSize - availSize;
290 mUsageBarPreference.clear();
291 mUsageBarPreference.addEntry(usedSize / (float) totalSize, android.graphics.Color.GRAY);
292 mUsageBarPreference.commit();
294 updatePreferencesFromState();
297 public void updateExact(long totalSize, long availSize, long appsSize, long downloadsSize,
298 long miscSize, long[] mediaSizes) {
299 mUsageBarPreference.clear();
301 mPreferences[TOTAL_SIZE].setSummary(formatSize(totalSize));
303 updatePreference(appsSize, totalSize, APPLICATIONS);
305 long totalMediaSize = 0;
306 for (int i = 0; i < sMediaCategories.length; i++) {
307 final int category = sMediaCategories[i].mCategory;
308 final long size = mediaSizes[i];
309 updatePreference(size, totalSize, category);
310 totalMediaSize += size;
313 updatePreference(downloadsSize, totalSize, DOWNLOADS);
315 // Note miscSize != totalSize - availSize - appsSize - downloadsSize - totalMediaSize
316 // Block size is taken into account. That can be extra space from folders. TODO Investigate
317 updatePreference(miscSize, totalSize, MISC);
319 updatePreference(availSize, totalSize, AVAILABLE);
321 mUsageBarPreference.commit();
324 private void updatePreference(long size, long totalSize, int category) {
326 mPreferences[category].setSummary(formatSize(size));
327 mUsageBarPreference.addEntry(size / (float) totalSize, mColors[category]);
329 removePreference(mPreferences[category]);
333 private void measure() {
334 mMeasurement.invalidate();
335 mMeasurement.measure();
338 public void onResume() {
339 mMeasurement.setReceiver(this);
343 public void onStorageStateChanged() {
347 public void onMediaScannerFinished() {
351 public void onPause() {
352 mMeasurement.cleanUp();
355 private static ShapeDrawable createRectShape(int width, int height, int color) {
356 ShapeDrawable shape = new ShapeDrawable(new RectShape());
357 shape.setIntrinsicHeight(height);
358 shape.setIntrinsicWidth(width);
359 shape.getPaint().setColor(color);
363 private String formatSize(long size) {
364 return Formatter.formatFileSize(getContext(), size);
368 public void updateApproximate(Bundle bundle) {
369 final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE);
370 message.setData(bundle);
371 mUpdateHandler.sendMessage(message);
375 public void updateExact(Bundle bundle) {
376 final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXACT);
377 message.setData(bundle);
378 mUpdateHandler.sendMessage(message);
381 public boolean mountToggleClicked(Preference preference) {
382 return preference == mMountTogglePreference;
385 public Intent intentForClick(Preference preference) {
386 Intent intent = null;
388 // TODO The current "delete" story is not fully handled by the respective applications.
389 // When it is done, make sure the intent types below are correct.
390 // If that cannot be done, remove these intents.
391 if (preference == mFormatPreference) {
392 intent = new Intent(Intent.ACTION_VIEW);
393 intent.setClass(getContext(), com.android.settings.MediaFormat.class);
394 } else if (preference == mPreferences[APPLICATIONS]) {
395 intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
396 intent.setClass(getContext(),
397 com.android.settings.Settings.ManageApplicationsActivity.class);
398 } else if (preference == mPreferences[DOWNLOADS]) {
399 intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra(
400 DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
401 } else if (preference == mPreferences[MUSIC]) {
402 intent = new Intent(Intent.ACTION_GET_CONTENT);
403 intent.setType("audio/mp3");
404 } else if (preference == mPreferences[DCIM]) {
405 intent = new Intent(Intent.ACTION_GET_CONTENT);
406 intent.setType("image/jpeg"); // TODO Create a Videos category, type = video/*
407 } else if (preference == mPreferences[MISC]) {
408 Context context = getContext().getApplicationContext();
409 if (mMeasurement.getMiscSize() > 0) {
410 intent = new Intent(context, MiscFilesHandler.class);
411 intent.putExtra(STORAGE_VOLUME, mStorageVolume);