OSDN Git Service

remove primary condition check on storage format.
[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.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;
39
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;
44
45 import java.util.HashMap;
46 import java.util.Iterator;
47 import java.util.List;
48
49 public class StorageVolumePreferenceCategory extends PreferenceCategory {
50     public static final String KEY_CACHE = "cache";
51
52     private static final int ORDER_USAGE_BAR = -2;
53     private static final int ORDER_STORAGE_LOW = -1;
54
55     /** Physical volume being measured, or {@code null} for internal. */
56     private final StorageVolume mVolume;
57     private final StorageMeasurement mMeasure;
58
59     private final Resources mResources;
60     private final StorageManager mStorageManager;
61     private final UserManager mUserManager;
62
63     private UsageBarPreference mUsageBarPreference;
64     private Preference mMountTogglePreference;
65     private Preference mFormatPreference;
66     private Preference mStorageLow;
67
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();
77
78     private boolean mUsbConnected;
79     private String mUsbFunction;
80
81     private long mTotalSize;
82
83     private static final int MSG_UI_UPDATE_APPROXIMATE = 1;
84     private static final int MSG_UI_UPDATE_DETAILS = 2;
85
86     private Handler mUpdateHandler = new Handler() {
87         @Override
88         public void handleMessage(Message msg) {
89             switch (msg.what) {
90                 case MSG_UI_UPDATE_APPROXIMATE: {
91                     final long[] size = (long[]) msg.obj;
92                     updateApproximate(size[0], size[1]);
93                     break;
94                 }
95                 case MSG_UI_UPDATE_DETAILS: {
96                     final MeasurementDetails details = (MeasurementDetails) msg.obj;
97                     updateDetails(details);
98                     break;
99                 }
100             }
101         }
102     };
103
104     /**
105      * Build category to summarize internal storage, including any emulated
106      * {@link StorageVolume}.
107      */
108     public static StorageVolumePreferenceCategory buildForInternal(Context context) {
109         return new StorageVolumePreferenceCategory(context, null);
110     }
111
112     /**
113      * Build category to summarize specific physical {@link StorageVolume}.
114      */
115     public static StorageVolumePreferenceCategory buildForPhysical(
116             Context context, StorageVolume volume) {
117         return new StorageVolumePreferenceCategory(context, volume);
118     }
119
120     private StorageVolumePreferenceCategory(Context context, StorageVolume volume) {
121         super(context);
122
123         mVolume = volume;
124         mMeasure = StorageMeasurement.getInstance(context, volume);
125
126         mResources = context.getResources();
127         mStorageManager = StorageManager.from(context);
128         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
129
130         setTitle(volume != null ? volume.getDescription(context)
131                 : context.getText(R.string.internal_storage));
132     }
133
134     private StorageItemPreference buildItem(int titleRes, int colorRes) {
135         return new StorageItemPreference(getContext(), titleRes, colorRes);
136     }
137
138     public void init() {
139         final Context context = getContext();
140
141         removeAll();
142
143         final UserInfo currentUser;
144         try {
145             currentUser = ActivityManagerNative.getDefault().getCurrentUser();
146         } catch (RemoteException e) {
147             throw new RuntimeException("Failed to get current user");
148         }
149
150         final List<UserInfo> otherUsers = getUsersExcluding(currentUser);
151         final boolean showUsers = mVolume == null && otherUsers.size() > 0;
152
153         mUsageBarPreference = new UsageBarPreference(context);
154         mUsageBarPreference.setOrder(ORDER_USAGE_BAR);
155         addPreference(mUsageBarPreference);
156
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);
161
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);
168
169         mItemCache.setKey(KEY_CACHE);
170
171         final boolean showDetails = mVolume == null || mVolume.isPrimary();
172         if (showDetails) {
173             if (showUsers) {
174                 addPreference(new PreferenceHeader(context, currentUser.name));
175             }
176
177             addPreference(mItemApps);
178             addPreference(mItemDcim);
179             addPreference(mItemMusic);
180             addPreference(mItemDownloads);
181             addPreference(mItemCache);
182             addPreference(mItemMisc);
183
184             if (showUsers) {
185                 addPreference(new PreferenceHeader(context, R.string.storage_other_users));
186
187                 int count = 0;
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);
195                 }
196             }
197         }
198
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);
202         if (isRemovable) {
203             mMountTogglePreference.setTitle(R.string.sd_eject);
204             mMountTogglePreference.setSummary(R.string.sd_eject_summary);
205             addPreference(mMountTogglePreference);
206         }
207
208         final boolean allowFormat = mVolume != null;
209         if (allowFormat) {
210             mFormatPreference = new Preference(context);
211             mFormatPreference.setTitle(R.string.sd_format);
212             mFormatPreference.setSummary(R.string.sd_format_summary);
213             addPreference(mFormatPreference);
214         }
215
216         final IPackageManager pm = ActivityThread.getPackageManager();
217         try {
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);
226                 mStorageLow = null;
227             }
228         } catch (RemoteException e) {
229         }
230     }
231
232     public StorageVolume getStorageVolume() {
233         return mVolume;
234     }
235
236     private void updatePreferencesFromState() {
237         // Only update for physical volumes
238         if (mVolume == null) return;
239
240         mMountTogglePreference.setEnabled(true);
241
242         final String state = mStorageManager.getVolumeState(mVolume.getPath());
243
244         if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
245             mItemAvailable.setTitle(R.string.memory_available_read_only);
246         } else {
247             mItemAvailable.setTitle(R.string.memory_available);
248         }
249
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));
255         } else {
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));
261             } else {
262                 mMountTogglePreference.setEnabled(false);
263                 mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
264                 mMountTogglePreference.setSummary(mResources.getString(R.string.sd_insert_summary));
265             }
266
267             removePreference(mUsageBarPreference);
268             removePreference(mItemTotal);
269             removePreference(mItemAvailable);
270         }
271
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));
279             }
280
281             if (mFormatPreference != null) {
282                 mFormatPreference.setEnabled(false);
283                 mFormatPreference.setSummary(mResources.getString(R.string.mtp_ptp_mode_summary));
284             }
285         } else if (mFormatPreference != null) {
286             mFormatPreference.setEnabled(true);
287             mFormatPreference.setSummary(mResources.getString(R.string.sd_format_summary));
288         }
289     }
290
291     public void updateApproximate(long totalSize, long availSize) {
292         mItemTotal.setSummary(formatSize(totalSize));
293         mItemAvailable.setSummary(formatSize(availSize));
294
295         mTotalSize = totalSize;
296
297         final long usedSize = totalSize - availSize;
298
299         mUsageBarPreference.clear();
300         mUsageBarPreference.addEntry(0, usedSize / (float) totalSize, android.graphics.Color.GRAY);
301         mUsageBarPreference.commit();
302
303         updatePreferencesFromState();
304     }
305
306     private static long totalValues(HashMap<String, Long> map, String... keys) {
307         long total = 0;
308         for (String key : keys) {
309             if (map.containsKey(key)) {
310                 total += map.get(key);
311             }
312         }
313         return total;
314     }
315
316     public void updateDetails(MeasurementDetails details) {
317         final boolean showDetails = mVolume == null || mVolume.isPrimary();
318         if (!showDetails) return;
319
320         // Count caches as available space, since system manages them
321         mItemTotal.setSummary(formatSize(details.totalSize));
322         mItemAvailable.setSummary(formatSize(details.availSize));
323
324         mUsageBarPreference.clear();
325
326         updatePreference(mItemApps, details.appsSize);
327
328         final long dcimSize = totalValues(details.mediaSize, Environment.DIRECTORY_DCIM,
329                 Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES);
330         updatePreference(mItemDcim, dcimSize);
331
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);
336
337         final long downloadsSize = totalValues(details.mediaSize, Environment.DIRECTORY_DOWNLOADS);
338         updatePreference(mItemDownloads, downloadsSize);
339
340         updatePreference(mItemCache, details.cacheSize);
341         updatePreference(mItemMisc, details.miscSize);
342
343         for (StorageItemPreference userPref : mItemUsers) {
344             final long userSize = details.usersSize.get(userPref.userHandle);
345             updatePreference(userPref, userSize);
346         }
347
348         mUsageBarPreference.commit();
349     }
350
351     private void updatePreference(StorageItemPreference pref, long size) {
352         if (size > 0) {
353             pref.setSummary(formatSize(size));
354             final int order = pref.getOrder();
355             mUsageBarPreference.addEntry(order, size / (float) mTotalSize, pref.color);
356         } else {
357             removePreference(pref);
358         }
359     }
360
361     private void measure() {
362         mMeasure.invalidate();
363         mMeasure.measure();
364     }
365
366     public void onResume() {
367         mMeasure.setReceiver(mReceiver);
368         measure();
369     }
370
371     public void onStorageStateChanged() {
372         init();
373         measure();
374     }
375
376     public void onUsbStateChanged(boolean isUsbConnected, String usbFunction) {
377         mUsbConnected = isUsbConnected;
378         mUsbFunction = usbFunction;
379         measure();
380     }
381
382     public void onMediaScannerFinished() {
383         measure();
384     }
385
386     public void onCacheCleared() {
387         measure();
388     }
389
390     public void onPause() {
391         mMeasure.cleanUp();
392     }
393
394     private String formatSize(long size) {
395         return Formatter.formatFileSize(getContext(), size);
396     }
397
398     private MeasurementReceiver mReceiver = new MeasurementReceiver() {
399         @Override
400         public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize) {
401             mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE, new long[] {
402                     totalSize, availSize }).sendToTarget();
403         }
404
405         @Override
406         public void updateDetails(StorageMeasurement meas, MeasurementDetails details) {
407             mUpdateHandler.obtainMessage(MSG_UI_UPDATE_DETAILS, details).sendToTarget();
408         }
409     };
410
411     public boolean mountToggleClicked(Preference preference) {
412         return preference == mMountTogglePreference;
413     }
414
415     public Intent intentForClick(Preference pref) {
416         Intent intent = null;
417
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);
445         }
446
447         return intent;
448     }
449
450     public static class PreferenceHeader extends Preference {
451         public PreferenceHeader(Context context, int titleRes) {
452             super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
453             setTitle(titleRes);
454         }
455
456         public PreferenceHeader(Context context, CharSequence title) {
457             super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
458             setTitle(title);
459         }
460
461         @Override
462         public boolean isEnabled() {
463             return false;
464         }
465     }
466
467     /**
468      * Return list of other users, excluding the current user.
469      */
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) {
475                 i.remove();
476             }
477         }
478         return users;
479     }
480 }