diff --git a/Android.mk b/Android.mk index 9c9a23a067b..fe8ed2d4909 100644 --- a/Android.mk +++ b/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_JAVA_LIBRARIES := bouncycastle telephony-common -LOCAL_STATIC_JAVA_LIBRARIES := guava android-support-v4 +LOCAL_STATIC_JAVA_LIBRARIES := guava android-support-v4 jsr305 LOCAL_MODULE_TAGS := optional diff --git a/res/values/strings.xml b/res/values/strings.xml index 8895de2593f..8bef5aa222c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1863,6 +1863,8 @@ SD card Available + + Available (read-only) Total space @@ -1916,7 +1918,6 @@ MTP or PTP function is active - \u0020(Read-only) Unmount USB storage? diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java index f6795897643..057f329786d 100644 --- a/src/com/android/settings/deviceinfo/Memory.java +++ b/src/com/android/settings/deviceinfo/Memory.java @@ -66,9 +66,9 @@ public class Memory extends SettingsPreferenceFragment { private static String sClickedMountPoint; // Access using getMountService() - private IMountService mMountService = null; - private StorageManager mStorageManager = null; - private UsbManager mUsbManager = null; + private IMountService mMountService; + private StorageManager mStorageManager; + private UsbManager mUsbManager; private ArrayList mCategories = Lists.newArrayList(); @@ -76,33 +76,28 @@ public class Memory extends SettingsPreferenceFragment { public void onCreate(Bundle icicle) { super.onCreate(icicle); - mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE); + final Context context = getActivity(); - if (mStorageManager == null) { - mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE); - mStorageManager.registerListener(mStorageListener); - } + mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); + + mStorageManager = StorageManager.from(context); + mStorageManager.registerListener(mStorageListener); addPreferencesFromResource(R.xml.device_info_memory); - if (!Environment.isExternalStorageEmulated()) { - // External storage is separate from internal storage; need to - // show internal storage as a separate item. - addCategoryForVolume(null); - } + addCategory(StorageVolumePreferenceCategory.buildForInternal(context)); final StorageVolume[] storageVolumes = mStorageManager.getVolumeList(); for (StorageVolume volume : storageVolumes) { - addCategoryForVolume(volume); + if (!volume.isEmulated()) { + addCategory(StorageVolumePreferenceCategory.buildForPhysical(context, volume)); + } } setHasOptionsMenu(true); } - private void addCategoryForVolume(StorageVolume volume) { - // TODO: Cluster multi-user emulated volumes into single category - final StorageVolumePreferenceCategory category = new StorageVolumePreferenceCategory( - getActivity(), volume); + private void addCategory(StorageVolumePreferenceCategory category) { mCategories.add(category); getPreferenceScreen().addPreference(category); category.init(); diff --git a/src/com/android/settings/deviceinfo/MiscFilesHandler.java b/src/com/android/settings/deviceinfo/MiscFilesHandler.java index 7f5c746d670..1e0cc46059f 100644 --- a/src/com/android/settings/deviceinfo/MiscFilesHandler.java +++ b/src/com/android/settings/deviceinfo/MiscFilesHandler.java @@ -20,7 +20,6 @@ import android.app.Activity; import android.app.ListActivity; import android.content.Context; import android.os.Bundle; -import android.os.UserHandle; import android.os.storage.StorageVolume; import android.text.format.Formatter; import android.util.Log; @@ -195,7 +194,7 @@ public class MiscFilesHandler extends ListActivity { final StorageVolume storageVolume = activity.getIntent().getParcelableExtra( StorageVolume.EXTRA_STORAGE_VOLUME); StorageMeasurement mMeasurement = StorageMeasurement.getInstance( - activity, storageVolume, UserHandle.CURRENT); + activity, storageVolume); if (mMeasurement == null) return; mData = (ArrayList) mMeasurement.mFileInfoForMisc; if (mData != null) { diff --git a/src/com/android/settings/deviceinfo/StorageItemPreference.java b/src/com/android/settings/deviceinfo/StorageItemPreference.java index 6a76a86af79..e4f3ff9b823 100644 --- a/src/com/android/settings/deviceinfo/StorageItemPreference.java +++ b/src/com/android/settings/deviceinfo/StorageItemPreference.java @@ -21,34 +21,38 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RectShape; +import android.os.UserHandle; import android.preference.Preference; import com.android.settings.R; public class StorageItemPreference extends Preference { + public final int color; + public final int userHandle; - private int mColor = Color.MAGENTA; - - public StorageItemPreference(Context context, String key, int titleRes, int colorRes) { - this(context, key, context.getText(titleRes), colorRes); + public StorageItemPreference(Context context, int titleRes, int colorRes) { + this(context, context.getText(titleRes), colorRes, UserHandle.USER_NULL); } - public StorageItemPreference(Context context, String key, CharSequence title, int colorRes) { + public StorageItemPreference( + Context context, CharSequence title, int colorRes, int userHandle) { super(context); - //setLayoutResource(R.layout.app_percentage_item); if (colorRes != 0) { - mColor = context.getResources().getColor(colorRes); + this.color = context.getResources().getColor(colorRes); final Resources res = context.getResources(); final int width = res.getDimensionPixelSize(R.dimen.device_memory_usage_button_width); final int height = res.getDimensionPixelSize(R.dimen.device_memory_usage_button_height); - setIcon(createRectShape(width, height, mColor)); + setIcon(createRectShape(width, height, this.color)); + } else { + this.color = Color.MAGENTA; } - setKey(key); setTitle(title); setSummary(R.string.memory_calculating_size); + + this.userHandle = userHandle; } private static ShapeDrawable createRectShape(int width, int height, int color) { @@ -58,8 +62,4 @@ public class StorageItemPreference extends Preference { shape.getPaint().setColor(color); return shape; } - - public int getColor() { - return mColor; - } } diff --git a/src/com/android/settings/deviceinfo/StorageMeasurement.java b/src/com/android/settings/deviceinfo/StorageMeasurement.java index e81bfd86ea3..50238f326dc 100644 --- a/src/com/android/settings/deviceinfo/StorageMeasurement.java +++ b/src/com/android/settings/deviceinfo/StorageMeasurement.java @@ -16,6 +16,7 @@ package com.android.settings.deviceinfo; +import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -24,7 +25,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageStatsObserver; import android.content.pm.PackageManager; import android.content.pm.PackageStats; -import android.os.Bundle; +import android.content.pm.UserInfo; import android.os.Environment; import android.os.Environment.UserEnvironment; import android.os.Handler; @@ -32,15 +33,15 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.os.storage.StorageVolume; import android.util.Log; -import android.util.Pair; +import android.util.SparseLongArray; import com.android.internal.app.IMediaContainerService; -import com.android.internal.util.Preconditions; import com.google.android.collect.Maps; +import com.google.common.collect.Sets; import java.io.File; import java.lang.ref.WeakReference; @@ -48,22 +49,14 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Set; + +import javax.annotation.concurrent.GuardedBy; /** - * Measure the memory for various systems. - * - * TODO: This class should ideally have less knowledge about what the context - * it's measuring is. In the future, reduce the amount of stuff it needs to - * know about by just keeping an array of measurement types of the following - * properties: - * - * Filesystem stats (using DefaultContainerService) - * Directory measurements (using DefaultContainerService.measureDir) - * Application measurements (using PackageManager) - * - * Then the calling application would just specify the type and an argument. - * This class would keep track of it while the calling application would - * decide on how to use it. + * Utility for measuring the disk usage of internal storage or a physical + * {@link StorageVolume}. Connects with a remote {@link IMediaContainerService} + * and delivers results to {@link MeasurementReceiver}. */ public class StorageMeasurement { private static final String TAG = "StorageMeasurement"; @@ -71,56 +64,99 @@ public class StorageMeasurement { private static final boolean LOCAL_LOGV = true; static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE); - public static final String TOTAL_SIZE = "total_size"; - - public static final String AVAIL_SIZE = "avail_size"; - - public static final String APPS_USED = "apps_used"; - - public static final String DOWNLOADS_SIZE = "downloads_size"; - - public static final String MISC_SIZE = "misc_size"; - - public static final String MEDIA_SIZES = "media_sizes"; - private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer"; public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService"); - private final MeasurementHandler mHandler; + /** Media types to measure on external storage. */ + private static final Set sMeasureMediaTypes = Sets.newHashSet( + Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES, + Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_MUSIC, + Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS, + Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS, + Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID); - private static HashMap, StorageMeasurement> - sInstances = Maps.newHashMap(); + @GuardedBy("sInstances") + private static HashMap 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;