sInstances = Maps.newHashMap();
+
+ /**
+ * Obtain shared instance of {@link StorageMeasurement} for given physical
+ * {@link StorageVolume}, or internal storage if {@code null}.
+ */
+ public static StorageMeasurement getInstance(Context context, StorageVolume volume) {
+ synchronized (sInstances) {
+ StorageMeasurement value = sInstances.get(volume);
+ if (value == null) {
+ value = new StorageMeasurement(context.getApplicationContext(), volume);
+ sInstances.put(volume, value);
+ }
+ return value;
+ }
+ }
+
+ public static class MeasurementDetails {
+ /**
+ * Total apps disk usage.
+ *
+ * When measuring internal storage, this value includes the code size of
+ * all apps (regardless of install status for current user), and
+ * internal disk used by the current user's apps. When the device
+ * emulates external storage, this value also includes emulated storage
+ * used by the current user's apps.
+ *
+ * When measuring a physical {@link StorageVolume}, this value includes
+ * usage by all apps on that volume.
+ */
+ public long appsSize;
+
+ /**
+ * Total media disk usage, categorized by types such as
+ * {@link Environment#DIRECTORY_MUSIC}.
+ *
+ * When measuring internal storage, this reflects media on emulated
+ * storage for the current user.
+ *
+ * When measuring a physical {@link StorageVolume}, this reflects media
+ * on that volume.
+ */
+ public HashMap mediaSize = Maps.newHashMap();
+
+ /**
+ * Misc external disk usage for the current user, unaccounted in
+ * {@link #mediaSize}.
+ */
+ public long miscSize;
+
+ /**
+ * Total disk usage for users, which is only meaningful for emulated
+ * internal storage. Key is {@link UserHandle}.
+ */
+ public SparseLongArray usersSize = new SparseLongArray();
+ }
+
+ public interface MeasurementReceiver {
+ public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize);
+ public void updateDetails(StorageMeasurement meas, MeasurementDetails details);
+ }
private volatile WeakReference mReceiver;
+ /** Physical volume being measured, or {@code null} for internal. */
+ private final StorageVolume mVolume;
+
+ private final boolean mIsInternal;
+ private final boolean mIsPrimary;
+
+ private final MeasurementHandler mHandler;
+
private long mTotalSize;
private long mAvailSize;
- private long mAppsSize;
- private long mDownloadsSize;
- private long mMiscSize;
- private long[] mMediaSizes = new long[StorageVolumePreferenceCategory.sMediaCategories.length];
-
- private final StorageVolume mStorageVolume;
- private final UserHandle mUser;
- private final UserEnvironment mUserEnv;
- private final boolean mIsPrimary;
- private final boolean mIsInternal;
-
- private boolean mIncludeAppCodeSize = true;
List mFileInfoForMisc;
- public interface MeasurementReceiver {
- public void updateApproximate(StorageMeasurement meas, Bundle bundle);
- public void updateExact(StorageMeasurement meas, Bundle bundle);
- }
-
- private StorageMeasurement(Context context, StorageVolume volume, UserHandle user) {
- mStorageVolume = volume;
- mUser = Preconditions.checkNotNull(user);
- mUserEnv = new UserEnvironment(mUser.getIdentifier());
+ private StorageMeasurement(Context context, StorageVolume volume) {
+ mVolume = volume;
mIsInternal = volume == null;
mIsPrimary = volume != null ? volume.isPrimary() : false;
@@ -130,35 +166,6 @@ public class StorageMeasurement {
mHandler = new MeasurementHandler(context, handlerThread.getLooper());
}
- public void setIncludeAppCodeSize(boolean include) {
- mIncludeAppCodeSize = include;
- }
-
- /**
- * Get the singleton of the StorageMeasurement class. The application
- * context is used to avoid leaking activities.
- * @param storageVolume The {@link StorageVolume} that will be measured
- * @param isPrimary true when this storage volume is the primary volume
- */
- public static StorageMeasurement getInstance(
- Context context, StorageVolume storageVolume, UserHandle user) {
- final Pair key = new Pair(
- storageVolume, user);
- synchronized (sInstances) {
- StorageMeasurement value = sInstances.get(key);
- if (value == null) {
- value = new StorageMeasurement(
- context.getApplicationContext(), storageVolume, user);
- sInstances.put(key, value);
- }
- return value;
- }
- }
-
- public UserHandle getUser() {
- return mUser;
- }
-
public void setReceiver(MeasurementReceiver receiver) {
if (mReceiver == null || mReceiver.get() == null) {
mReceiver = new WeakReference(receiver);
@@ -186,15 +193,10 @@ public class StorageMeasurement {
if (receiver == null) {
return;
}
-
- Bundle bundle = new Bundle();
- bundle.putLong(TOTAL_SIZE, mTotalSize);
- bundle.putLong(AVAIL_SIZE, mAvailSize);
-
- receiver.updateApproximate(this, bundle);
+ receiver.updateApproximate(this, mTotalSize, mAvailSize);
}
- private void sendExactUpdate() {
+ private void sendExactUpdate(MeasurementDetails details) {
MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
if (receiver == null) {
if (LOGV) {
@@ -202,27 +204,76 @@ public class StorageMeasurement {
}
return;
}
+ receiver.updateDetails(this, details);
+ }
- Bundle bundle = new Bundle();
- bundle.putLong(TOTAL_SIZE, mTotalSize);
- bundle.putLong(AVAIL_SIZE, mAvailSize);
- bundle.putLong(APPS_USED, mAppsSize);
- bundle.putLong(DOWNLOADS_SIZE, mDownloadsSize);
- bundle.putLong(MISC_SIZE, mMiscSize);
- bundle.putLongArray(MEDIA_SIZES, mMediaSizes);
+ private static class StatsObserver extends IPackageStatsObserver.Stub {
+ private final boolean mIsInternal;
+ private final MeasurementDetails mDetails;
+ private final int mCurrentUser;
+ private final Message mFinished;
- receiver.updateExact(this, bundle);
+ private int mRemaining;
+
+ public StatsObserver(boolean isInternal, MeasurementDetails details, int currentUser,
+ Message finished, int remaining) {
+ mIsInternal = isInternal;
+ mDetails = details;
+ mCurrentUser = currentUser;
+ mFinished = finished;
+ mRemaining = remaining;
+ }
+
+ @Override
+ public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
+ synchronized (mDetails) {
+ if (succeeded) {
+ addStatsLocked(stats);
+ }
+ if (--mRemaining == 0) {
+ mFinished.sendToTarget();
+ }
+ }
+ }
+
+ private void addStatsLocked(PackageStats stats) {
+ final long externalSize = stats.externalCodeSize + stats.externalDataSize
+ + stats.externalCacheSize + stats.externalMediaSize;
+
+ if (mIsInternal) {
+ final long codeSize;
+ final long dataSize;
+ if (Environment.isExternalStorageEmulated()) {
+ // OBB is shared on emulated storage, so count once as code,
+ // and data includes emulated storage.
+ codeSize = stats.codeSize + stats.externalObbSize;
+ dataSize = stats.dataSize + externalSize;
+ } else {
+ codeSize = stats.codeSize;
+ dataSize = stats.dataSize;
+ }
+
+ // Include code and combined data for current user
+ if (stats.userHandle == mCurrentUser) {
+ mDetails.appsSize += codeSize;
+ mDetails.appsSize += dataSize;
+ }
+
+ // Include combined data for user summary
+ addValue(mDetails.usersSize, stats.userHandle, dataSize);
+
+ } else {
+ // Physical storage; only count external sizes
+ mDetails.appsSize += externalSize + stats.externalObbSize;
+ }
+ }
}
private class MeasurementHandler extends Handler {
public static final int MSG_MEASURE = 1;
-
public static final int MSG_CONNECTED = 2;
-
public static final int MSG_DISCONNECT = 3;
-
public static final int MSG_COMPLETED = 4;
-
public static final int MSG_INVALIDATE = 5;
private Object mLock = new Object();
@@ -231,21 +282,21 @@ public class StorageMeasurement {
private volatile boolean mBound = false;
- private volatile boolean mMeasured = false;
-
- private StatsObserver mStatsObserver;
+ private MeasurementDetails mCached;
private final WeakReference mContext;
- final private ServiceConnection mDefContainerConn = new ServiceConnection() {
+ private final ServiceConnection mDefContainerConn = new ServiceConnection() {
+ @Override
public void onServiceConnected(ComponentName name, IBinder service) {
- final IMediaContainerService imcs = IMediaContainerService.Stub
- .asInterface(service);
+ final IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(
+ service);
mDefaultContainer = imcs;
mBound = true;
sendMessage(obtainMessage(MSG_CONNECTED, imcs));
}
+ @Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
removeMessages(MSG_CONNECTED);
@@ -261,8 +312,8 @@ public class StorageMeasurement {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_MEASURE: {
- if (mMeasured) {
- sendExactUpdate();
+ if (mCached != null) {
+ sendExactUpdate(mCached);
break;
}
@@ -277,8 +328,8 @@ public class StorageMeasurement {
sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
} else {
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
- context.bindService(service, mDefContainerConn,
- Context.BIND_AUTO_CREATE, mUser.getIdentifier());
+ context.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
+ UserHandle.USER_OWNER);
}
}
break;
@@ -304,89 +355,19 @@ public class StorageMeasurement {
break;
}
case MSG_COMPLETED: {
- mMeasured = true;
- sendExactUpdate();
+ mCached = (MeasurementDetails) msg.obj;
+ sendExactUpdate(mCached);
break;
}
case MSG_INVALIDATE: {
- mMeasured = false;
+ mCached = null;
break;
}
}
}
- /**
- * Request measurement of each package.
- *
- * @param pm PackageManager instance to query
- */
- public void requestQueuedMeasurementsLocked(PackageManager pm) {
- final String[] appsList = mStatsObserver.getAppsList();
- final int N = appsList.length;
- for (int i = 0; i < N; i++) {
- pm.getPackageSizeInfo(appsList[i], mStatsObserver);
- }
- }
-
- private class StatsObserver extends IPackageStatsObserver.Stub {
- private long mAppsSizeForThisStatsObserver = 0;
- private final List mAppsList = new ArrayList();
-
- public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
- if (!mStatsObserver.equals(this)) {
- // this callback's class object is no longer in use. ignore this callback.
- return;
- }
-
- if (succeeded) {
- if (mIsInternal) {
- if (mIncludeAppCodeSize) {
- mAppsSizeForThisStatsObserver += stats.codeSize;
- }
- mAppsSizeForThisStatsObserver += stats.dataSize;
- } else if (!Environment.isExternalStorageEmulated()) {
- mAppsSizeForThisStatsObserver += stats.externalObbSize +
- stats.externalCodeSize + stats.externalDataSize +
- stats.externalCacheSize + stats.externalMediaSize;
- } else {
- if (mIncludeAppCodeSize) {
- mAppsSizeForThisStatsObserver += stats.codeSize;
- }
- mAppsSizeForThisStatsObserver += stats.dataSize +
- stats.externalCodeSize + stats.externalDataSize +
- stats.externalCacheSize + stats.externalMediaSize +
- stats.externalObbSize;
- }
- }
-
- synchronized (mAppsList) {
- mAppsList.remove(stats.packageName);
- if (mAppsList.size() > 0) return;
- }
-
- mAppsSize = mAppsSizeForThisStatsObserver;
- onInternalMeasurementComplete();
- }
-
- public void queuePackageMeasurementLocked(String packageName) {
- synchronized (mAppsList) {
- mAppsList.add(packageName);
- }
- }
-
- public String[] getAppsList() {
- synchronized (mAppsList) {
- return mAppsList.toArray(new String[mAppsList.size()]);
- }
- }
- }
-
- private void onInternalMeasurementComplete() {
- sendEmptyMessage(MSG_COMPLETED);
- }
-
private void measureApproximateStorage(IMediaContainerService imcs) {
- final String path = mStorageVolume != null ? mStorageVolume.getPath()
+ final String path = mVolume != null ? mVolume.getPath()
: Environment.getDataDirectory().getPath();
try {
final long[] stats = imcs.getFileSystemStats(path);
@@ -400,146 +381,116 @@ public class StorageMeasurement {
}
private void measureExactStorage(IMediaContainerService imcs) {
- Context context = mContext != null ? mContext.get() : null;
+ final Context context = mContext != null ? mContext.get() : null;
if (context == null) {
return;
}
- // Media
- for (int i = 0; i < StorageVolumePreferenceCategory.sMediaCategories.length; i++) {
- if (mIsPrimary) {
- String[] dirs = StorageVolumePreferenceCategory.sMediaCategories[i].mDirPaths;
- final int length = dirs.length;
- mMediaSizes[i] = 0;
- for (int d = 0; d < length; d++) {
- final String path = dirs[d];
- mMediaSizes[i] += getDirectorySize(imcs, path);
- }
- } else {
- // TODO Compute sizes using the MediaStore
- mMediaSizes[i] = 0;
+ final MeasurementDetails details = new MeasurementDetails();
+ final Message finished = obtainMessage(MSG_COMPLETED, details);
+
+ final UserManager userManager = (UserManager) context.getSystemService(
+ Context.USER_SERVICE);
+ final List users = userManager.getUsers();
+
+ final int currentUser = ActivityManager.getCurrentUser();
+ final UserEnvironment currentEnv = new UserEnvironment(currentUser);
+
+ // Measure media types for emulated storage, or for primary physical
+ // external volume
+ final boolean measureMedia = (mIsInternal && Environment.isExternalStorageEmulated())
+ || mIsPrimary;
+ if (measureMedia) {
+ for (String type : sMeasureMediaTypes) {
+ final File path = currentEnv.getExternalStoragePublicDirectory(type);
+ final long size = getDirectorySize(imcs, path);
+ details.mediaSize.put(type, size);
}
}
- /* Compute sizes using the media provider
- // Media sizes are measured by the MediaStore. Query database.
- ContentResolver contentResolver = context.getContentResolver();
- // TODO "external" as a static String from MediaStore?
- Uri audioUri = MediaStore.Files.getContentUri("external");
- final String[] projection =
- new String[] { "sum(" + MediaStore.Files.FileColumns.SIZE + ")" };
- final String selection =
- MediaStore.Files.FileColumns.STORAGE_ID + "=" +
- Integer.toString(mStorageVolume.getStorageId()) + " AND " +
- MediaStore.Files.FileColumns.MEDIA_TYPE + "=?";
-
- for (int i = 0; i < StorageVolumePreferenceCategory.sMediaCategories.length; i++) {
- mMediaSizes[i] = 0;
- int mediaType = StorageVolumePreferenceCategory.sMediaCategories[i].mediaType;
- Cursor c = null;
- try {
- c = contentResolver.query(audioUri, projection, selection,
- new String[] { Integer.toString(mediaType) } , null);
-
- if (c != null && c.moveToNext()) {
- long size = c.getLong(0);
- mMediaSizes[i] = size;
- }
- } finally {
- if (c != null) c.close();
- }
- }
- */
-
- // Downloads (primary volume only)
- if (mIsPrimary) {
- final String downloadsPath = mUserEnv.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
- mDownloadsSize = getDirectorySize(imcs, downloadsPath);
- } else {
- mDownloadsSize = 0;
+ // Measure misc files not counted under media
+ if (mIsInternal || mIsPrimary) {
+ final File path = mIsInternal ? currentEnv.getExternalStorageDirectory()
+ : mVolume.getPathFile();
+ details.miscSize = measureMisc(imcs, path);
}
- // Misc
- mMiscSize = 0;
- if (mIsPrimary) {
- measureSizesOfMisc(imcs);
+ // Measure total emulated storage of all users; internal apps data
+ // will be spliced in later
+ for (UserInfo user : users) {
+ final UserEnvironment userEnv = new UserEnvironment(user.id);
+ final long size = getDirectorySize(imcs, userEnv.getExternalStorageDirectory());
+ addValue(details.usersSize, user.id, size);
}
- // Apps
- // We have to get installd to measure the package sizes.
- PackageManager pm = context.getPackageManager();
- if (pm == null) {
- return;
- }
- final List apps;
- if (mIsPrimary || mIsInternal) {
- apps = pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES |
- PackageManager.GET_DISABLED_COMPONENTS);
- } else {
- // TODO also measure apps installed on the SD card
- apps = Collections.emptyList();
- }
+ // Measure all apps for all users
+ final PackageManager pm = context.getPackageManager();
+ if (mIsInternal || mIsPrimary) {
+ final List apps = pm.getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS);
- if (apps != null && apps.size() > 0) {
- // initiate measurement of all package sizes. need new StatsObserver object.
- mStatsObserver = new StatsObserver();
- synchronized (mStatsObserver.mAppsList) {
- for (int i = 0; i < apps.size(); i++) {
- final ApplicationInfo info = apps.get(i);
- mStatsObserver.queuePackageMeasurementLocked(info.packageName);
+ final int count = users.size() * apps.size();
+ final StatsObserver observer = new StatsObserver(
+ mIsInternal, details, currentUser, finished, count);
+
+ for (UserInfo user : users) {
+ for (ApplicationInfo app : apps) {
+ pm.getPackageSizeInfo(app.packageName, user.id, observer);
}
}
- requestQueuedMeasurementsLocked(pm);
- // Sending of the message back to the MeasurementReceiver is
- // completed in the PackageObserver
} else {
- onInternalMeasurementComplete();
+ finished.sendToTarget();
}
}
}
- private long getDirectorySize(IMediaContainerService imcs, String dir) {
+ private static long getDirectorySize(IMediaContainerService imcs, File path) {
try {
- return imcs.calculateDirectorySize(dir);
+ final long size = imcs.calculateDirectorySize(path.toString());
+ Log.d(TAG, "getDirectorySize(" + path + ") returned " + size);
+ return size;
} catch (Exception e) {
- Log.w(TAG, "Could not read memory from default container service for " + dir, e);
+ Log.w(TAG, "Could not read memory from default container service for " + path, e);
return 0;
}
}
- long getMiscSize() {
- return mMiscSize;
- }
-
- private void measureSizesOfMisc(IMediaContainerService imcs) {
- File top = new File(mStorageVolume.getPath());
+ private long measureMisc(IMediaContainerService imcs, File dir) {
mFileInfoForMisc = new ArrayList();
- File[] files = top.listFiles();
- if (files == null) return;
- final int len = files.length;
- // Get sizes of all top level nodes except the ones already computed...
+
+ final File[] files = dir.listFiles();
+ if (files == null) return 0;
+
+ // Get sizes of all top level nodes except the ones already computed
long counter = 0;
- for (int i = 0; i < len; i++) {
- String path = files[i].getAbsolutePath();
- if (StorageVolumePreferenceCategory.sPathsExcludedForMisc.contains(path)) {
+ long miscSize = 0;
+
+ for (File file : files) {
+ final String path = file.getAbsolutePath();
+ final String name = file.getName();
+ if (sMeasureMediaTypes.contains(name)) {
continue;
}
- if (files[i].isFile()) {
- final long fileSize = files[i].length();
+
+ if (file.isFile()) {
+ final long fileSize = file.length();
mFileInfoForMisc.add(new FileInfo(path, fileSize, counter++));
- mMiscSize += fileSize;
- } else if (files[i].isDirectory()) {
- final long dirSize = getDirectorySize(imcs, path);
+ miscSize += fileSize;
+ } else if (file.isDirectory()) {
+ final long dirSize = getDirectorySize(imcs, file);
mFileInfoForMisc.add(new FileInfo(path, dirSize, counter++));
- mMiscSize += dirSize;
+ miscSize += dirSize;
} else {
// Non directory, non file: not listed
}
}
+
// sort the list of FileInfo objects collected above in descending order of their sizes
Collections.sort(mFileInfoForMisc);
+
+ return miscSize;
}
static class FileInfo implements Comparable {
@@ -565,10 +516,7 @@ public class StorageMeasurement {
}
}
- /**
- * TODO remove this method, only used because external SD Card needs a special treatment.
- */
- boolean isExternalSDCard() {
- return !mIsPrimary && !mIsInternal;
+ private static void addValue(SparseLongArray array, int key, long value) {
+ array.put(key, array.get(key) + value);
}
}
diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
index 5be9e151ede..469dbc77d40 100644
--- a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
+++ b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
@@ -25,13 +25,10 @@ import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
-import android.os.Bundle;
import android.os.Environment;
-import android.os.Environment.UserEnvironment;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
@@ -40,179 +37,98 @@ import android.preference.PreferenceCategory;
import android.text.format.Formatter;
import com.android.settings.R;
+import com.android.settings.deviceinfo.StorageMeasurement.MeasurementDetails;
import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
import com.google.android.collect.Lists;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
-
-public class StorageVolumePreferenceCategory extends PreferenceCategory
- implements MeasurementReceiver {
- private static final String KEY_TOTAL_SIZE = "total_size";
- private static final String KEY_AVAILABLE = "available";
-
- private static final String KEY_APPLICATIONS = "applications";
- private static final String KEY_DCIM = "dcim"; // Pictures and Videos
- private static final String KEY_MUSIC = "music";
- private static final String KEY_DOWNLOADS = "downloads";
- private static final String KEY_MISC = "misc";
- private static final String KEY_USER_PREFIX = "user";
+public class StorageVolumePreferenceCategory extends PreferenceCategory {
private static final int ORDER_USAGE_BAR = -2;
private static final int ORDER_STORAGE_LOW = -1;
- private Preference mItemTotal;
- private Preference mItemAvailable;
+ /** Physical volume being measured, or {@code null} for internal. */
+ private final StorageVolume mVolume;
+ private final StorageMeasurement mMeasure;
+
+ private final Resources mResources;
+ private final StorageManager mStorageManager;
+ private final UserManager mUserManager;
private UsageBarPreference mUsageBarPreference;
private Preference mMountTogglePreference;
private Preference mFormatPreference;
private Preference mStorageLow;
- private final StorageVolume mVolume;
-
- private final boolean mIsEmulated;
- private final boolean mIsPrimary;
-
- private final Resources mResources;
- private final StorageManager mStorageManager;
- private final UserManager mUserManager;
-
- @Deprecated
- private static final UserEnvironment sOwnerEnv = new UserEnvironment(UserHandle.USER_OWNER);
-
- /** Measurement for local user. */
- private StorageMeasurement mLocalMeasure;
- /** All used measurements, including other users. */
- private List mAllMeasures = Lists.newArrayList();
-
- private boolean mAllowFormat;
+ private StorageItemPreference mItemTotal;
+ private StorageItemPreference mItemAvailable;
+ private StorageItemPreference mItemApps;
+ private StorageItemPreference mItemDcim;
+ private StorageItemPreference mItemMusic;
+ private StorageItemPreference mItemDownloads;
+ private StorageItemPreference mItemMisc;
+ private List mItemUsers = Lists.newArrayList();
private boolean mUsbConnected;
private String mUsbFunction;
- private boolean mShowingApprox;
- public static final Set sPathsExcludedForMisc = new HashSet();
+ private long mTotalSize;
- static class MediaCategory {
- final String[] mDirPaths;
- final String mCategory;
-
- public MediaCategory(String category, String... directories) {
- mCategory = category;
- final int length = directories.length;
- mDirPaths = new String[length];
- for (int i = 0; i < length; i++) {
- final String name = directories[i];
- final String path = sOwnerEnv.getExternalStoragePublicDirectory(name).
- getAbsolutePath();
- mDirPaths[i] = path;
- sPathsExcludedForMisc.add(path);
- }
- }
- }
-
- static final MediaCategory[] sMediaCategories = new MediaCategory[] {
- new MediaCategory(KEY_DCIM, Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES,
- Environment.DIRECTORY_PICTURES),
- new MediaCategory(KEY_MUSIC, Environment.DIRECTORY_MUSIC, Environment.DIRECTORY_ALARMS,
- Environment.DIRECTORY_NOTIFICATIONS, Environment.DIRECTORY_RINGTONES,
- Environment.DIRECTORY_PODCASTS)
- };
-
- static {
- // Downloads
- sPathsExcludedForMisc.add(sOwnerEnv.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());
- // Apps
- sPathsExcludedForMisc.add(sOwnerEnv.getExternalStorageDirectory().getAbsolutePath() +
- "/Android");
- }
-
- // Updates the memory usage bar graph.
private static final int MSG_UI_UPDATE_APPROXIMATE = 1;
-
- // Updates the memory usage bar graph.
- private static final int MSG_UI_UPDATE_EXACT = 2;
+ private static final int MSG_UI_UPDATE_DETAILS = 2;
private Handler mUpdateHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UI_UPDATE_APPROXIMATE: {
- final UserHandle user = (UserHandle) msg.obj;
- final Bundle bundle = msg.getData();
- final long totalSize = bundle.getLong(StorageMeasurement.TOTAL_SIZE);
- final long availSize = bundle.getLong(StorageMeasurement.AVAIL_SIZE);
-
- if (user.getIdentifier() == UserHandle.USER_CURRENT) {
- updateApproximate(totalSize, availSize);
- }
+ final long[] size = (long[]) msg.obj;
+ updateApproximate(size[0], size[1]);
break;
}
- case MSG_UI_UPDATE_EXACT: {
- final UserHandle user = (UserHandle) msg.obj;
- final Bundle bundle = msg.getData();
- final long totalSize = bundle.getLong(StorageMeasurement.TOTAL_SIZE);
- final long availSize = bundle.getLong(StorageMeasurement.AVAIL_SIZE);
- final long appsUsed = bundle.getLong(StorageMeasurement.APPS_USED);
- final long downloadsSize = bundle.getLong(StorageMeasurement.DOWNLOADS_SIZE);
- final long miscSize = bundle.getLong(StorageMeasurement.MISC_SIZE);
- final long[] mediaSizes = bundle.getLongArray(StorageMeasurement.MEDIA_SIZES);
-
- if (user.getIdentifier() == UserHandle.USER_CURRENT) {
- updateExact(totalSize, availSize, appsUsed, downloadsSize, miscSize,
- mediaSizes);
- } else {
- long usedSize = appsUsed + downloadsSize + miscSize;
- for (long mediaSize : mediaSizes) {
- usedSize += mediaSize;
- }
- updateUserExact(user, totalSize, usedSize);
- }
+ case MSG_UI_UPDATE_DETAILS: {
+ final MeasurementDetails details = (MeasurementDetails) msg.obj;
+ updateDetails(details);
break;
}
}
}
};
- public StorageVolumePreferenceCategory(Context context, StorageVolume volume) {
+ /**
+ * Build category to summarize internal storage, including any emulated
+ * {@link StorageVolume}.
+ */
+ public static StorageVolumePreferenceCategory buildForInternal(Context context) {
+ return new StorageVolumePreferenceCategory(context, null);
+ }
+
+ /**
+ * Build category to summarize specific physical {@link StorageVolume}.
+ */
+ public static StorageVolumePreferenceCategory buildForPhysical(
+ Context context, StorageVolume volume) {
+ return new StorageVolumePreferenceCategory(context, volume);
+ }
+
+ private StorageVolumePreferenceCategory(Context context, StorageVolume volume) {
super(context);
mVolume = volume;
-
- mIsPrimary = volume != null ? volume.isPrimary() : false;
- mIsEmulated = volume != null ? volume.isEmulated() : false;
+ mMeasure = StorageMeasurement.getInstance(context, volume);
mResources = context.getResources();
- mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+ mStorageManager = StorageManager.from(context);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
setTitle(volume != null ? volume.getDescription(context)
: context.getText(R.string.internal_storage));
-
- mLocalMeasure = StorageMeasurement.getInstance(context, volume, UserHandle.CURRENT);
- mAllMeasures.add(mLocalMeasure);
-
- // Cannot format emulated storage
- mAllowFormat = mVolume != null && !mVolume.isEmulated();
-
- // For now we are disabling reformatting secondary external storage
- // until some interoperability problems with MTP are fixed
- if (!mIsPrimary) mAllowFormat = false;
}
- private StorageItemPreference addStorageItem(String key, int titleRes, int colorRes) {
- final StorageItemPreference pref = new StorageItemPreference(
- getContext(), key, titleRes, colorRes);
- addPreference(pref);
- return pref;
- }
-
- private static String buildUserKey(UserHandle user) {
- return KEY_USER_PREFIX + user.getIdentifier();
+ private StorageItemPreference buildItem(int titleRes, int colorRes) {
+ return new StorageItemPreference(getContext(), titleRes, colorRes);
}
public void init() {
@@ -226,51 +142,62 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
}
final List otherUsers = getUsersExcluding(currentUser);
- final boolean measureUsers = mIsEmulated && mIsPrimary && otherUsers.size() > 0;
+ final boolean showUsers = mVolume == null && otherUsers.size() > 0;
mUsageBarPreference = new UsageBarPreference(context);
mUsageBarPreference.setOrder(ORDER_USAGE_BAR);
addPreference(mUsageBarPreference);
- mItemTotal = addStorageItem(KEY_TOTAL_SIZE, R.string.memory_size, 0);
- mItemAvailable = addStorageItem(
- KEY_AVAILABLE, R.string.memory_available, R.color.memory_avail);
+ mItemTotal = buildItem(R.string.memory_size, 0);
+ mItemAvailable = buildItem(R.string.memory_available, R.color.memory_avail);
+ addPreference(mItemTotal);
+ addPreference(mItemAvailable);
- if (measureUsers) {
- addPreference(new PreferenceHeader(context, currentUser.name));
- }
+ mItemApps = buildItem(R.string.memory_apps_usage, R.color.memory_apps_usage);
+ mItemDcim = buildItem(R.string.memory_dcim_usage, R.color.memory_dcim);
+ mItemMusic = buildItem(R.string.memory_music_usage, R.color.memory_music);
+ mItemDownloads = buildItem(R.string.memory_downloads_usage, R.color.memory_downloads);
+ mItemMisc = buildItem(R.string.memory_media_misc_usage, R.color.memory_misc);
- addStorageItem(KEY_APPLICATIONS, R.string.memory_apps_usage, R.color.memory_apps_usage);
- addStorageItem(KEY_DCIM, R.string.memory_dcim_usage, R.color.memory_dcim);
- addStorageItem(KEY_MUSIC, R.string.memory_music_usage, R.color.memory_music);
- addStorageItem(KEY_DOWNLOADS, R.string.memory_downloads_usage, R.color.memory_downloads);
- addStorageItem(KEY_MISC, R.string.memory_media_misc_usage, R.color.memory_misc);
+ final boolean showDetails = mVolume == null || mVolume.isPrimary();
+ if (showDetails) {
+ if (showUsers) {
+ addPreference(new PreferenceHeader(context, currentUser.name));
+ }
- if (measureUsers) {
- addPreference(new PreferenceHeader(context, R.string.storage_other_users));
+ addPreference(mItemApps);
+ addPreference(mItemDcim);
+ addPreference(mItemMusic);
+ addPreference(mItemDownloads);
+ addPreference(mItemMisc);
- int count = 0;
- for (UserInfo info : otherUsers) {
- final UserHandle user = new UserHandle(info.id);
- final String key = buildUserKey(user);
+ if (showUsers) {
+ addPreference(new PreferenceHeader(context, R.string.storage_other_users));
- final StorageMeasurement measure = StorageMeasurement.getInstance(
- context, mVolume, user);
- measure.setIncludeAppCodeSize(false);
- mAllMeasures.add(measure);
-
- final int colorRes = count++ % 2 == 0 ? R.color.memory_user_light
- : R.color.memory_user_dark;
- addPreference(new StorageItemPreference(getContext(), key, info.name, colorRes));
+ int count = 0;
+ for (UserInfo info : otherUsers) {
+ final int colorRes = count++ % 2 == 0 ? R.color.memory_user_light
+ : R.color.memory_user_dark;
+ final StorageItemPreference userPref = new StorageItemPreference(
+ getContext(), info.name, colorRes, info.id);
+ mItemUsers.add(userPref);
+ addPreference(userPref);
+ }
}
}
- mMountTogglePreference = new Preference(context);
- mMountTogglePreference.setTitle(R.string.sd_eject);
- mMountTogglePreference.setSummary(R.string.sd_eject_summary);
- addPreference(mMountTogglePreference);
+ final boolean isRemovable = mVolume != null ? mVolume.isRemovable() : false;
+ if (isRemovable) {
+ mMountTogglePreference = new Preference(context);
+ mMountTogglePreference.setTitle(R.string.sd_eject);
+ mMountTogglePreference.setSummary(R.string.sd_eject_summary);
+ addPreference(mMountTogglePreference);
+ }
- if (mAllowFormat) {
+ // Only allow formatting of primary physical storage
+ // TODO: enable for non-primary volumes once MTP is fixed
+ final boolean allowFormat = mVolume != null ? mVolume.isPrimary() : false;
+ if (allowFormat) {
mFormatPreference = new Preference(context);
mFormatPreference.setTitle(R.string.sd_format);
mFormatPreference.setSummary(R.string.sd_format_summary);
@@ -298,33 +225,24 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
}
private void updatePreferencesFromState() {
+ // Only update for physical volumes
+ if (mVolume == null) return;
+
mMountTogglePreference.setEnabled(true);
- String state = mVolume != null
- ? mStorageManager.getVolumeState(mVolume.getPath())
- : Environment.MEDIA_MOUNTED;
+ final String state = mStorageManager.getVolumeState(mVolume.getPath());
- String readOnly = "";
if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
- state = Environment.MEDIA_MOUNTED;
- readOnly = mResources.getString(R.string.read_only);
+ mItemAvailable.setSummary(R.string.memory_available_read_only);
if (mFormatPreference != null) {
removePreference(mFormatPreference);
}
+ } else {
+ mItemAvailable.setSummary(R.string.memory_available);
}
- if ((mVolume == null || !mVolume.isRemovable())
- && !Environment.MEDIA_UNMOUNTED.equals(state)) {
- // This device has built-in storage that is not removable.
- // There is no reason for the user to unmount it.
- removePreference(mMountTogglePreference);
- }
-
- if (Environment.MEDIA_MOUNTED.equals(state)) {
- // TODO: create better i18n strings here; we might end up appending
- // multiple times
- mItemAvailable.setSummary(mItemAvailable.getSummary() + readOnly);
-
+ if (Environment.MEDIA_MOUNTED.equals(state)
+ || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
mMountTogglePreference.setEnabled(true);
mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary));
@@ -351,8 +269,10 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
if (mUsbConnected && (UsbManager.USB_FUNCTION_MTP.equals(mUsbFunction) ||
UsbManager.USB_FUNCTION_PTP.equals(mUsbFunction))) {
mMountTogglePreference.setEnabled(false);
- if (Environment.MEDIA_MOUNTED.equals(state)) {
- mMountTogglePreference.setSummary(mResources.getString(R.string.mtp_ptp_mode_summary));
+ if (Environment.MEDIA_MOUNTED.equals(state)
+ || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
+ mMountTogglePreference.setSummary(
+ mResources.getString(R.string.mtp_ptp_mode_summary));
}
if (mFormatPreference != null) {
@@ -363,99 +283,78 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
mFormatPreference.setEnabled(true);
mFormatPreference.setSummary(mResources.getString(R.string.sd_format_summary));
}
-
}
public void updateApproximate(long totalSize, long availSize) {
mItemTotal.setSummary(formatSize(totalSize));
mItemAvailable.setSummary(formatSize(availSize));
+ mTotalSize = totalSize;
+
final long usedSize = totalSize - availSize;
mUsageBarPreference.clear();
mUsageBarPreference.addEntry(0, usedSize / (float) totalSize, android.graphics.Color.GRAY);
mUsageBarPreference.commit();
- mShowingApprox = true;
updatePreferencesFromState();
}
- public void updateExact(long totalSize, long availSize, long appsSize, long downloadsSize,
- long miscSize, long[] mediaSizes) {
- if (mShowingApprox) {
- mUsageBarPreference.clear();
- mShowingApprox = false;
+ private static long totalValues(HashMap map, String... keys) {
+ long total = 0;
+ for (String key : keys) {
+ total += map.get(key);
}
+ return total;
+ }
- mItemTotal.setSummary(formatSize(totalSize));
+ public void updateDetails(MeasurementDetails details) {
+ final boolean showDetails = mVolume == null || mVolume.isPrimary();
+ if (!showDetails) return;
- if (mLocalMeasure.isExternalSDCard()) {
- // TODO FIXME: external SD card will not report any size. Show used space in bar graph
- final long usedSize = totalSize - availSize;
- mUsageBarPreference.addEntry(
- 0, usedSize / (float) totalSize, android.graphics.Color.GRAY);
+ mUsageBarPreference.clear();
+
+ updatePreference(mItemApps, details.appsSize);
+
+ final long dcimSize = totalValues(details.mediaSize, Environment.DIRECTORY_DCIM,
+ Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES);
+ updatePreference(mItemDcim, dcimSize);
+
+ final long musicSize = totalValues(details.mediaSize, Environment.DIRECTORY_MUSIC,
+ Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
+ Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
+ updatePreference(mItemMusic, musicSize);
+
+ final long downloadsSize = totalValues(details.mediaSize, Environment.DIRECTORY_DOWNLOADS);
+ updatePreference(mItemDownloads, musicSize);
+
+ updatePreference(mItemMisc, details.miscSize);
+
+ for (StorageItemPreference userPref : mItemUsers) {
+ final long userSize = details.usersSize.get(userPref.userHandle);
+ updatePreference(userPref, userSize);
}
- updatePreference(appsSize, totalSize, KEY_APPLICATIONS);
-
- long totalMediaSize = 0;
- for (int i = 0; i < sMediaCategories.length; i++) {
- final String category = sMediaCategories[i].mCategory;
- final long size = mediaSizes[i];
- updatePreference(size, totalSize, category);
- totalMediaSize += size;
- }
-
- updatePreference(downloadsSize, totalSize, KEY_DOWNLOADS);
-
- // Note miscSize != totalSize - availSize - appsSize - downloadsSize - totalMediaSize
- // Block size is taken into account. That can be extra space from folders. TODO Investigate
- updatePreference(miscSize, totalSize, KEY_MISC);
-
- mItemAvailable.setSummary(formatSize(availSize));
-
mUsageBarPreference.commit();
}
- public void updateUserExact(UserHandle user, long totalSize, long usedSize) {
- if (mShowingApprox) {
- mUsageBarPreference.clear();
- mShowingApprox = false;
- }
-
- final String key = buildUserKey(user);
-
- findPreference(key).setSummary(formatSize(usedSize));
- updatePreference(usedSize, totalSize, key);
-
- mUsageBarPreference.commit();
- }
-
- private void updatePreference(long size, long totalSize, String category) {
- final StorageItemPreference pref = (StorageItemPreference) findPreference(category);
-
- if (pref != null) {
- if (size > 0) {
- pref.setSummary(formatSize(size));
- final int order = pref.getOrder();
- mUsageBarPreference.addEntry(order, size / (float) totalSize, pref.getColor());
- } else {
- removePreference(pref);
- }
+ private void updatePreference(StorageItemPreference pref, long size) {
+ if (size > 0) {
+ pref.setSummary(formatSize(size));
+ final int order = pref.getOrder();
+ mUsageBarPreference.addEntry(order, size / (float) mTotalSize, pref.color);
+ } else {
+ removePreference(pref);
}
}
private void measure() {
- for (StorageMeasurement measure : mAllMeasures) {
- measure.invalidate();
- measure.measure();
- }
+ mMeasure.invalidate();
+ mMeasure.measure();
}
public void onResume() {
- for (StorageMeasurement measure : mAllMeasures) {
- measure.setReceiver(this);
- }
+ mMeasure.setReceiver(mReceiver);
measure();
}
@@ -474,30 +373,25 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
}
public void onPause() {
- for (StorageMeasurement measure : mAllMeasures) {
- measure.cleanUp();
- }
+ mMeasure.cleanUp();
}
private String formatSize(long size) {
return Formatter.formatFileSize(getContext(), size);
}
- @Override
- public void updateApproximate(StorageMeasurement meas, Bundle bundle) {
- final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE);
- message.obj = meas.getUser();
- message.setData(bundle);
- mUpdateHandler.sendMessage(message);
- }
+ private MeasurementReceiver mReceiver = new MeasurementReceiver() {
+ @Override
+ public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize) {
+ mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE, new long[] {
+ totalSize, availSize }).sendToTarget();
+ }
- @Override
- public void updateExact(StorageMeasurement meas, Bundle bundle) {
- final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXACT);
- message.obj = meas.getUser();
- message.setData(bundle);
- mUpdateHandler.sendMessage(message);
- }
+ @Override
+ public void updateDetails(StorageMeasurement meas, MeasurementDetails details) {
+ mUpdateHandler.obtainMessage(MSG_UI_UPDATE_DETAILS, details).sendToTarget();
+ }
+ };
public boolean mountToggleClicked(Preference preference) {
return preference == mMountTogglePreference;
@@ -514,27 +408,25 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
intent = new Intent(Intent.ACTION_VIEW);
intent.setClass(getContext(), com.android.settings.MediaFormat.class);
intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
- } else if (KEY_APPLICATIONS.equals(key)) {
+ } else if (pref == mItemApps) {
intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
intent.setClass(getContext(),
com.android.settings.Settings.ManageApplicationsActivity.class);
- } else if (KEY_DOWNLOADS.equals(key)) {
+ } else if (pref == mItemDownloads) {
intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra(
DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
- } else if (KEY_MUSIC.equals(key)) {
+ } else if (pref == mItemMusic) {
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("audio/mp3");
- } else if (KEY_DCIM.equals(key)) {
+ } else if (pref == mItemDcim) {
intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
// TODO Create a Videos category, type = vnd.android.cursor.dir/video
intent.setType("vnd.android.cursor.dir/image");
- } else if (KEY_MISC.equals(key)) {
+ } else if (pref == mItemMisc) {
Context context = getContext().getApplicationContext();
- if (mLocalMeasure.getMiscSize() > 0) {
- intent = new Intent(context, MiscFilesHandler.class);
- intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
- }
+ intent = new Intent(context, MiscFilesHandler.class);
+ intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
}
return intent;