OSDN Git Service

7b52bcbd1a660713b978920bd781d6ce836b70a2
[android-x86/packages-apps-Settings.git] / src / com / android / settings / deviceinfo / StorageVolumePreferenceCategory.java
1 /*
2  * Copyright (C) 2011 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.settings.deviceinfo;
18
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;
35
36 import com.android.settings.R;
37 import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
38
39 import java.util.HashSet;
40 import java.util.Set;
41
42 public class StorageVolumePreferenceCategory extends PreferenceCategory implements
43         MeasurementReceiver {
44
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;
52
53     private UsageBarPreference mUsageBarPreference;
54     private Preference[] mPreferences;
55     private Preference mMountTogglePreference;
56     private Preference mFormatPreference;
57     private int[] mColors;
58
59     private Resources mResources;
60
61     private StorageVolume mStorageVolume;
62
63     private StorageMeasurement mMeasurement;
64
65     static class CategoryInfo {
66         final int mTitle;
67         final int mColor;
68
69         public CategoryInfo(int title, int color) {
70             mTitle = title;
71             mColor = color;
72         }
73     }
74
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),
83     };
84
85     public static final Set<String> sPathsExcludedForMisc = new HashSet<String>();
86
87     static class MediaCategory {
88         final String[] mDirPaths;
89         final int mCategory;
90         //final int mMediaType;
91
92         public MediaCategory(int category, String... directories) {
93             mCategory = category;
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).
99                         getAbsolutePath();
100                 mDirPaths[i] = path;
101                 sPathsExcludedForMisc.add(path);
102             }
103         }
104     }
105
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)
112     };
113
114     static {
115         // Downloads
116         sPathsExcludedForMisc.add(Environment.getExternalStoragePublicDirectory(
117                 Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());
118         // Apps
119         sPathsExcludedForMisc.add(Environment.getExternalStorageDirectory().getAbsolutePath() +
120                 "/Android");
121     }
122
123     // Updates the memory usage bar graph.
124     private static final int MSG_UI_UPDATE_APPROXIMATE = 1;
125
126     // Updates the memory usage bar graph.
127     private static final int MSG_UI_UPDATE_EXACT = 2;
128
129     // Key for the extra StorageVolume bundle added to the Misc intent.
130     static final String STORAGE_VOLUME = "storage_volume";
131
132     private Handler mUpdateHandler = new Handler() {
133         @Override
134         public void handleMessage(Message msg) {
135             switch (msg.what) {
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);
141                     break;
142                 }
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,
152                             mediaSizes);
153                     break;
154                 }
155             }
156         }
157     };
158
159     public StorageVolumePreferenceCategory(Context context, Resources resources,
160             StorageVolume storageVolume, boolean isPrimary) {
161         super(context);
162         mResources = resources;
163         mStorageVolume = storageVolume;
164         setTitle(storageVolume.getDescription());
165         mMeasurement = StorageMeasurement.getInstance(context, storageVolume, isPrimary);
166         mMeasurement.setReceiver(this);
167     }
168
169     public void init() {
170         mUsageBarPreference = new UsageBarPreference(getContext());
171
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);
174
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]));
187             }
188         }
189
190         mMountTogglePreference = new Preference(getContext());
191         mMountTogglePreference.setTitle(R.string.sd_eject);
192         mMountTogglePreference.setSummary(R.string.sd_eject_summary);
193
194         mFormatPreference = new Preference(getContext());
195         mFormatPreference.setTitle(R.string.sd_format);
196         mFormatPreference.setSummary(R.string.sd_format_summary);
197     }
198
199     public String getMountPoint() {
200         return mStorageVolume.getPath();
201     }
202
203     public String getStorageVolumeState() {
204         try {
205             IMountService mountService =
206                 IMountService.Stub.asInterface(ServiceManager.getService("mount"));
207             return mountService.getVolumeState(getMountPoint());
208         } catch (Exception rex) {
209             return Environment.MEDIA_REMOVED;
210         }
211     }
212
213     /**
214      * Successive mounts can change the list of visible preferences.
215      * This makes sure all preferences are visible and displayed in the right order.
216      */
217     private void resetPreferences() {
218         final int numberOfCategories = sCategoryInfos.length;
219
220         removePreference(mUsageBarPreference);
221         for (int i = 0; i < numberOfCategories; i++) {
222             removePreference(mPreferences[i]);
223         }
224         removePreference(mMountTogglePreference);
225         removePreference(mFormatPreference);
226
227         addPreference(mUsageBarPreference);
228         for (int i = 0; i < numberOfCategories; i++) {
229             addPreference(mPreferences[i]);
230         }
231         addPreference(mMountTogglePreference);
232         addPreference(mFormatPreference);
233
234         mMountTogglePreference.setEnabled(true);
235     }
236
237     private void updatePreferencesFromState() {
238         resetPreferences();
239
240         String state = getStorageVolumeState();
241
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);
247         }
248
249         if (mStorageVolume.isEmulated()) {
250             removePreference(mFormatPreference);
251         }
252
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);
257         }
258
259         if (state.equals(Environment.MEDIA_MOUNTED)) {
260             mPreferences[AVAILABLE].setSummary(mPreferences[AVAILABLE].getSummary() + readOnly);
261
262             mMountTogglePreference.setEnabled(true);
263             mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
264             mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary));
265         } else {
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));
271             } else {
272                 mMountTogglePreference.setEnabled(false);
273                 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
274                 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_insert_summary));
275             }
276
277             removePreference(mUsageBarPreference);
278             removePreference(mPreferences[TOTAL_SIZE]);
279             removePreference(mPreferences[AVAILABLE]);
280             removePreference(mFormatPreference);
281         }
282     }
283
284     public void updateApproximate(long totalSize, long availSize) {
285         mPreferences[TOTAL_SIZE].setSummary(formatSize(totalSize));
286         mPreferences[AVAILABLE].setSummary(formatSize(availSize));
287
288         final long usedSize = totalSize - availSize;
289
290         mUsageBarPreference.clear();
291         mUsageBarPreference.addEntry(usedSize / (float) totalSize, android.graphics.Color.GRAY);
292         mUsageBarPreference.commit();
293
294         updatePreferencesFromState();
295     }
296
297     public void updateExact(long totalSize, long availSize, long appsSize, long downloadsSize,
298             long miscSize, long[] mediaSizes) {
299         mUsageBarPreference.clear();
300
301         mPreferences[TOTAL_SIZE].setSummary(formatSize(totalSize));
302
303         updatePreference(appsSize, totalSize, APPLICATIONS);
304
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;
311         }
312
313         updatePreference(downloadsSize, totalSize, DOWNLOADS);
314
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);
318
319         updatePreference(availSize, totalSize, AVAILABLE);
320
321         mUsageBarPreference.commit();
322     }
323
324     private void updatePreference(long size, long totalSize, int category) {
325         if (size > 0) {
326             mPreferences[category].setSummary(formatSize(size));
327             mUsageBarPreference.addEntry(size / (float) totalSize, mColors[category]);
328         } else {
329             removePreference(mPreferences[category]);
330         }
331     }
332
333     private void measure() {
334         mMeasurement.invalidate();
335         mMeasurement.measure();
336     }
337
338     public void onResume() {
339         mMeasurement.setReceiver(this);
340         measure();
341     }
342
343     public void onStorageStateChanged() {
344         measure();
345     }
346
347     public void onMediaScannerFinished() {
348         measure();
349     }
350
351     public void onPause() {
352         mMeasurement.cleanUp();
353     }
354
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);
360         return shape;
361     }
362
363     private String formatSize(long size) {
364         return Formatter.formatFileSize(getContext(), size);
365     }
366
367     @Override
368     public void updateApproximate(Bundle bundle) {
369         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE);
370         message.setData(bundle);
371         mUpdateHandler.sendMessage(message);
372     }
373
374     @Override
375     public void updateExact(Bundle bundle) {
376         final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXACT);
377         message.setData(bundle);
378         mUpdateHandler.sendMessage(message);
379     }
380
381     public boolean mountToggleClicked(Preference preference) {
382         return preference == mMountTogglePreference;
383     }
384
385     public Intent intentForClick(Preference preference) {
386         Intent intent = null;
387
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);
412             }
413         }
414
415         return intent;
416     }
417 }