Iteration on multi-user Storage UI.
Now that DefaultContainerService has visibility of external storage for all users, we can measure internal storage in a single pass. Internal storage measurement now iterates across all known users, counting both apps and emulated storage usage. Create MeasurementDetails object with documentation about what is counted under various device configurations. Generalize to measure each Environment.DIRECTORY separately, so it can be combined as needed. General cleanup of how measurements are passed to UI. Bug: 7003520 Change-Id: Ib89c185296a0c9debdc20beeaa98584d803a84e8
This commit is contained in:
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
|
|||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_JAVA_LIBRARIES := bouncycastle telephony-common
|
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
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
|
||||||
|
@@ -1863,6 +1863,8 @@
|
|||||||
<string name="sd_memory" product="default">SD card</string>
|
<string name="sd_memory" product="default">SD card</string>
|
||||||
<!-- SD card & phone storage settings title. The amount of free space for some storage partition. For example, this is listed under both the "Internal phone storage" section and the "SD card" section. -->
|
<!-- SD card & phone storage settings title. The amount of free space for some storage partition. For example, this is listed under both the "Internal phone storage" section and the "SD card" section. -->
|
||||||
<string name="memory_available">Available</string>
|
<string name="memory_available">Available</string>
|
||||||
|
<!-- SD card & phone storage settings title. The amount of free space for some storage partition when the volume is read-only. [CHAR LIMIT=64] -->
|
||||||
|
<string name="memory_available_read_only">Available (read-only)</string>
|
||||||
<!-- SD card & phone storage settings screen heading. The total amount of storage space for some storage partition. For example, this is listed under both the "Internal phone storage" section and the "SD card" section -->
|
<!-- SD card & phone storage settings screen heading. The total amount of storage space for some storage partition. For example, this is listed under both the "Internal phone storage" section and the "SD card" section -->
|
||||||
<string name="memory_size">Total space</string>
|
<string name="memory_size">Total space</string>
|
||||||
<!-- SD card & phone storage settings summary. Displayed when the total memory usage is being calculated. Will be replaced with a number like "12.3 GB" when finished calucating. [CHAR LIMIT=30] -->
|
<!-- SD card & phone storage settings summary. Displayed when the total memory usage is being calculated. Will be replaced with a number like "12.3 GB" when finished calucating. [CHAR LIMIT=30] -->
|
||||||
@@ -1916,7 +1918,6 @@
|
|||||||
<!-- SD card & phone storage settings item summary that will result in the phone connected to PC and MTP/PTP enabled. [CHAR LIMIT=80] -->
|
<!-- SD card & phone storage settings item summary that will result in the phone connected to PC and MTP/PTP enabled. [CHAR LIMIT=80] -->
|
||||||
<string name="mtp_ptp_mode_summary">MTP or PTP function is active</string>
|
<string name="mtp_ptp_mode_summary">MTP or PTP function is active</string>
|
||||||
|
|
||||||
<string name="read_only">\u0020(Read-only)</string>
|
|
||||||
<!-- SD card eject confirmation dialog title [CHAR LIMIT=25] -->
|
<!-- SD card eject confirmation dialog title [CHAR LIMIT=25] -->
|
||||||
<string name="dlg_confirm_unmount_title" product="nosdcard">Unmount USB storage?</string>
|
<string name="dlg_confirm_unmount_title" product="nosdcard">Unmount USB storage?</string>
|
||||||
<!-- SD card eject confirmation dialog title -->
|
<!-- SD card eject confirmation dialog title -->
|
||||||
|
@@ -66,9 +66,9 @@ public class Memory extends SettingsPreferenceFragment {
|
|||||||
private static String sClickedMountPoint;
|
private static String sClickedMountPoint;
|
||||||
|
|
||||||
// Access using getMountService()
|
// Access using getMountService()
|
||||||
private IMountService mMountService = null;
|
private IMountService mMountService;
|
||||||
private StorageManager mStorageManager = null;
|
private StorageManager mStorageManager;
|
||||||
private UsbManager mUsbManager = null;
|
private UsbManager mUsbManager;
|
||||||
|
|
||||||
private ArrayList<StorageVolumePreferenceCategory> mCategories = Lists.newArrayList();
|
private ArrayList<StorageVolumePreferenceCategory> mCategories = Lists.newArrayList();
|
||||||
|
|
||||||
@@ -76,33 +76,28 @@ public class Memory extends SettingsPreferenceFragment {
|
|||||||
public void onCreate(Bundle icicle) {
|
public void onCreate(Bundle icicle) {
|
||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
|
|
||||||
mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
|
final Context context = getActivity();
|
||||||
|
|
||||||
if (mStorageManager == null) {
|
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
|
||||||
mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
|
|
||||||
mStorageManager.registerListener(mStorageListener);
|
mStorageManager = StorageManager.from(context);
|
||||||
}
|
mStorageManager.registerListener(mStorageListener);
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.device_info_memory);
|
addPreferencesFromResource(R.xml.device_info_memory);
|
||||||
|
|
||||||
if (!Environment.isExternalStorageEmulated()) {
|
addCategory(StorageVolumePreferenceCategory.buildForInternal(context));
|
||||||
// External storage is separate from internal storage; need to
|
|
||||||
// show internal storage as a separate item.
|
|
||||||
addCategoryForVolume(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
final StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
|
final StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
|
||||||
for (StorageVolume volume : storageVolumes) {
|
for (StorageVolume volume : storageVolumes) {
|
||||||
addCategoryForVolume(volume);
|
if (!volume.isEmulated()) {
|
||||||
|
addCategory(StorageVolumePreferenceCategory.buildForPhysical(context, volume));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCategoryForVolume(StorageVolume volume) {
|
private void addCategory(StorageVolumePreferenceCategory category) {
|
||||||
// TODO: Cluster multi-user emulated volumes into single category
|
|
||||||
final StorageVolumePreferenceCategory category = new StorageVolumePreferenceCategory(
|
|
||||||
getActivity(), volume);
|
|
||||||
mCategories.add(category);
|
mCategories.add(category);
|
||||||
getPreferenceScreen().addPreference(category);
|
getPreferenceScreen().addPreference(category);
|
||||||
category.init();
|
category.init();
|
||||||
|
@@ -20,7 +20,6 @@ import android.app.Activity;
|
|||||||
import android.app.ListActivity;
|
import android.app.ListActivity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.os.storage.StorageVolume;
|
import android.os.storage.StorageVolume;
|
||||||
import android.text.format.Formatter;
|
import android.text.format.Formatter;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -195,7 +194,7 @@ public class MiscFilesHandler extends ListActivity {
|
|||||||
final StorageVolume storageVolume = activity.getIntent().getParcelableExtra(
|
final StorageVolume storageVolume = activity.getIntent().getParcelableExtra(
|
||||||
StorageVolume.EXTRA_STORAGE_VOLUME);
|
StorageVolume.EXTRA_STORAGE_VOLUME);
|
||||||
StorageMeasurement mMeasurement = StorageMeasurement.getInstance(
|
StorageMeasurement mMeasurement = StorageMeasurement.getInstance(
|
||||||
activity, storageVolume, UserHandle.CURRENT);
|
activity, storageVolume);
|
||||||
if (mMeasurement == null) return;
|
if (mMeasurement == null) return;
|
||||||
mData = (ArrayList<StorageMeasurement.FileInfo>) mMeasurement.mFileInfoForMisc;
|
mData = (ArrayList<StorageMeasurement.FileInfo>) mMeasurement.mFileInfoForMisc;
|
||||||
if (mData != null) {
|
if (mData != null) {
|
||||||
|
@@ -21,34 +21,38 @@ import android.content.res.Resources;
|
|||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.ShapeDrawable;
|
import android.graphics.drawable.ShapeDrawable;
|
||||||
import android.graphics.drawable.shapes.RectShape;
|
import android.graphics.drawable.shapes.RectShape;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
|
||||||
public class StorageItemPreference extends Preference {
|
public class StorageItemPreference extends Preference {
|
||||||
|
public final int color;
|
||||||
|
public final int userHandle;
|
||||||
|
|
||||||
private int mColor = Color.MAGENTA;
|
public StorageItemPreference(Context context, int titleRes, int colorRes) {
|
||||||
|
this(context, context.getText(titleRes), colorRes, UserHandle.USER_NULL);
|
||||||
public StorageItemPreference(Context context, String key, int titleRes, int colorRes) {
|
|
||||||
this(context, key, context.getText(titleRes), colorRes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public StorageItemPreference(Context context, String key, CharSequence title, int colorRes) {
|
public StorageItemPreference(
|
||||||
|
Context context, CharSequence title, int colorRes, int userHandle) {
|
||||||
super(context);
|
super(context);
|
||||||
//setLayoutResource(R.layout.app_percentage_item);
|
|
||||||
|
|
||||||
if (colorRes != 0) {
|
if (colorRes != 0) {
|
||||||
mColor = context.getResources().getColor(colorRes);
|
this.color = context.getResources().getColor(colorRes);
|
||||||
|
|
||||||
final Resources res = context.getResources();
|
final Resources res = context.getResources();
|
||||||
final int width = res.getDimensionPixelSize(R.dimen.device_memory_usage_button_width);
|
final int width = res.getDimensionPixelSize(R.dimen.device_memory_usage_button_width);
|
||||||
final int height = res.getDimensionPixelSize(R.dimen.device_memory_usage_button_height);
|
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);
|
setTitle(title);
|
||||||
setSummary(R.string.memory_calculating_size);
|
setSummary(R.string.memory_calculating_size);
|
||||||
|
|
||||||
|
this.userHandle = userHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ShapeDrawable createRectShape(int width, int height, int color) {
|
private static ShapeDrawable createRectShape(int width, int height, int color) {
|
||||||
@@ -58,8 +62,4 @@ public class StorageItemPreference extends Preference {
|
|||||||
shape.getPaint().setColor(color);
|
shape.getPaint().setColor(color);
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getColor() {
|
|
||||||
return mColor;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.settings.deviceinfo;
|
package com.android.settings.deviceinfo;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -24,7 +25,7 @@ import android.content.pm.ApplicationInfo;
|
|||||||
import android.content.pm.IPackageStatsObserver;
|
import android.content.pm.IPackageStatsObserver;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageStats;
|
import android.content.pm.PackageStats;
|
||||||
import android.os.Bundle;
|
import android.content.pm.UserInfo;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.Environment.UserEnvironment;
|
import android.os.Environment.UserEnvironment;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -32,15 +33,15 @@ import android.os.HandlerThread;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.os.UserManager;
|
||||||
import android.os.storage.StorageVolume;
|
import android.os.storage.StorageVolume;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.SparseLongArray;
|
||||||
|
|
||||||
import com.android.internal.app.IMediaContainerService;
|
import com.android.internal.app.IMediaContainerService;
|
||||||
import com.android.internal.util.Preconditions;
|
|
||||||
import com.google.android.collect.Maps;
|
import com.google.android.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
@@ -48,22 +49,14 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Measure the memory for various systems.
|
* Utility for measuring the disk usage of internal storage or a physical
|
||||||
*
|
* {@link StorageVolume}. Connects with a remote {@link IMediaContainerService}
|
||||||
* TODO: This class should ideally have less knowledge about what the context
|
* and delivers results to {@link MeasurementReceiver}.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
public class StorageMeasurement {
|
public class StorageMeasurement {
|
||||||
private static final String TAG = "StorageMeasurement";
|
private static final String TAG = "StorageMeasurement";
|
||||||
@@ -71,56 +64,99 @@ public class StorageMeasurement {
|
|||||||
private static final boolean LOCAL_LOGV = true;
|
private static final boolean LOCAL_LOGV = true;
|
||||||
static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
|
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";
|
private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
|
||||||
|
|
||||||
public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
|
public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
|
||||||
DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService");
|
DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService");
|
||||||
|
|
||||||
private final MeasurementHandler mHandler;
|
/** Media types to measure on external storage. */
|
||||||
|
private static final Set<String> 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<Pair<StorageVolume, UserHandle>, StorageMeasurement>
|
@GuardedBy("sInstances")
|
||||||
sInstances = Maps.newHashMap();
|
private static HashMap<StorageVolume, StorageMeasurement> 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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}.
|
||||||
|
* <p>
|
||||||
|
* When measuring internal storage, this reflects media on emulated
|
||||||
|
* storage for the current user.
|
||||||
|
* <p>
|
||||||
|
* When measuring a physical {@link StorageVolume}, this reflects media
|
||||||
|
* on that volume.
|
||||||
|
*/
|
||||||
|
public HashMap<String, Long> 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<MeasurementReceiver> mReceiver;
|
private volatile WeakReference<MeasurementReceiver> 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 mTotalSize;
|
||||||
private long mAvailSize;
|
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<FileInfo> mFileInfoForMisc;
|
List<FileInfo> mFileInfoForMisc;
|
||||||
|
|
||||||
public interface MeasurementReceiver {
|
private StorageMeasurement(Context context, StorageVolume volume) {
|
||||||
public void updateApproximate(StorageMeasurement meas, Bundle bundle);
|
mVolume = volume;
|
||||||
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());
|
|
||||||
mIsInternal = volume == null;
|
mIsInternal = volume == null;
|
||||||
mIsPrimary = volume != null ? volume.isPrimary() : false;
|
mIsPrimary = volume != null ? volume.isPrimary() : false;
|
||||||
|
|
||||||
@@ -130,35 +166,6 @@ public class StorageMeasurement {
|
|||||||
mHandler = new MeasurementHandler(context, handlerThread.getLooper());
|
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<StorageVolume, UserHandle> key = new Pair<StorageVolume, UserHandle>(
|
|
||||||
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) {
|
public void setReceiver(MeasurementReceiver receiver) {
|
||||||
if (mReceiver == null || mReceiver.get() == null) {
|
if (mReceiver == null || mReceiver.get() == null) {
|
||||||
mReceiver = new WeakReference<MeasurementReceiver>(receiver);
|
mReceiver = new WeakReference<MeasurementReceiver>(receiver);
|
||||||
@@ -186,15 +193,10 @@ public class StorageMeasurement {
|
|||||||
if (receiver == null) {
|
if (receiver == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
receiver.updateApproximate(this, mTotalSize, mAvailSize);
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putLong(TOTAL_SIZE, mTotalSize);
|
|
||||||
bundle.putLong(AVAIL_SIZE, mAvailSize);
|
|
||||||
|
|
||||||
receiver.updateApproximate(this, bundle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendExactUpdate() {
|
private void sendExactUpdate(MeasurementDetails details) {
|
||||||
MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
|
MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
|
||||||
if (receiver == null) {
|
if (receiver == null) {
|
||||||
if (LOGV) {
|
if (LOGV) {
|
||||||
@@ -202,27 +204,76 @@ public class StorageMeasurement {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
receiver.updateDetails(this, details);
|
||||||
|
}
|
||||||
|
|
||||||
Bundle bundle = new Bundle();
|
private static class StatsObserver extends IPackageStatsObserver.Stub {
|
||||||
bundle.putLong(TOTAL_SIZE, mTotalSize);
|
private final boolean mIsInternal;
|
||||||
bundle.putLong(AVAIL_SIZE, mAvailSize);
|
private final MeasurementDetails mDetails;
|
||||||
bundle.putLong(APPS_USED, mAppsSize);
|
private final int mCurrentUser;
|
||||||
bundle.putLong(DOWNLOADS_SIZE, mDownloadsSize);
|
private final Message mFinished;
|
||||||
bundle.putLong(MISC_SIZE, mMiscSize);
|
|
||||||
bundle.putLongArray(MEDIA_SIZES, mMediaSizes);
|
|
||||||
|
|
||||||
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 {
|
private class MeasurementHandler extends Handler {
|
||||||
public static final int MSG_MEASURE = 1;
|
public static final int MSG_MEASURE = 1;
|
||||||
|
|
||||||
public static final int MSG_CONNECTED = 2;
|
public static final int MSG_CONNECTED = 2;
|
||||||
|
|
||||||
public static final int MSG_DISCONNECT = 3;
|
public static final int MSG_DISCONNECT = 3;
|
||||||
|
|
||||||
public static final int MSG_COMPLETED = 4;
|
public static final int MSG_COMPLETED = 4;
|
||||||
|
|
||||||
public static final int MSG_INVALIDATE = 5;
|
public static final int MSG_INVALIDATE = 5;
|
||||||
|
|
||||||
private Object mLock = new Object();
|
private Object mLock = new Object();
|
||||||
@@ -231,21 +282,21 @@ public class StorageMeasurement {
|
|||||||
|
|
||||||
private volatile boolean mBound = false;
|
private volatile boolean mBound = false;
|
||||||
|
|
||||||
private volatile boolean mMeasured = false;
|
private MeasurementDetails mCached;
|
||||||
|
|
||||||
private StatsObserver mStatsObserver;
|
|
||||||
|
|
||||||
private final WeakReference<Context> mContext;
|
private final WeakReference<Context> mContext;
|
||||||
|
|
||||||
final private ServiceConnection mDefContainerConn = new ServiceConnection() {
|
private final ServiceConnection mDefContainerConn = new ServiceConnection() {
|
||||||
|
@Override
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
final IMediaContainerService imcs = IMediaContainerService.Stub
|
final IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(
|
||||||
.asInterface(service);
|
service);
|
||||||
mDefaultContainer = imcs;
|
mDefaultContainer = imcs;
|
||||||
mBound = true;
|
mBound = true;
|
||||||
sendMessage(obtainMessage(MSG_CONNECTED, imcs));
|
sendMessage(obtainMessage(MSG_CONNECTED, imcs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
mBound = false;
|
mBound = false;
|
||||||
removeMessages(MSG_CONNECTED);
|
removeMessages(MSG_CONNECTED);
|
||||||
@@ -261,8 +312,8 @@ public class StorageMeasurement {
|
|||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case MSG_MEASURE: {
|
case MSG_MEASURE: {
|
||||||
if (mMeasured) {
|
if (mCached != null) {
|
||||||
sendExactUpdate();
|
sendExactUpdate(mCached);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,8 +328,8 @@ public class StorageMeasurement {
|
|||||||
sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
|
sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
|
||||||
} else {
|
} else {
|
||||||
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
|
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
|
||||||
context.bindService(service, mDefContainerConn,
|
context.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
|
||||||
Context.BIND_AUTO_CREATE, mUser.getIdentifier());
|
UserHandle.USER_OWNER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -304,89 +355,19 @@ public class StorageMeasurement {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MSG_COMPLETED: {
|
case MSG_COMPLETED: {
|
||||||
mMeasured = true;
|
mCached = (MeasurementDetails) msg.obj;
|
||||||
sendExactUpdate();
|
sendExactUpdate(mCached);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MSG_INVALIDATE: {
|
case MSG_INVALIDATE: {
|
||||||
mMeasured = false;
|
mCached = null;
|
||||||
break;
|
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<String> mAppsList = new ArrayList<String>();
|
|
||||||
|
|
||||||
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) {
|
private void measureApproximateStorage(IMediaContainerService imcs) {
|
||||||
final String path = mStorageVolume != null ? mStorageVolume.getPath()
|
final String path = mVolume != null ? mVolume.getPath()
|
||||||
: Environment.getDataDirectory().getPath();
|
: Environment.getDataDirectory().getPath();
|
||||||
try {
|
try {
|
||||||
final long[] stats = imcs.getFileSystemStats(path);
|
final long[] stats = imcs.getFileSystemStats(path);
|
||||||
@@ -400,146 +381,116 @@ public class StorageMeasurement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void measureExactStorage(IMediaContainerService imcs) {
|
private void measureExactStorage(IMediaContainerService imcs) {
|
||||||
Context context = mContext != null ? mContext.get() : null;
|
final Context context = mContext != null ? mContext.get() : null;
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Media
|
final MeasurementDetails details = new MeasurementDetails();
|
||||||
for (int i = 0; i < StorageVolumePreferenceCategory.sMediaCategories.length; i++) {
|
final Message finished = obtainMessage(MSG_COMPLETED, details);
|
||||||
if (mIsPrimary) {
|
|
||||||
String[] dirs = StorageVolumePreferenceCategory.sMediaCategories[i].mDirPaths;
|
final UserManager userManager = (UserManager) context.getSystemService(
|
||||||
final int length = dirs.length;
|
Context.USER_SERVICE);
|
||||||
mMediaSizes[i] = 0;
|
final List<UserInfo> users = userManager.getUsers();
|
||||||
for (int d = 0; d < length; d++) {
|
|
||||||
final String path = dirs[d];
|
final int currentUser = ActivityManager.getCurrentUser();
|
||||||
mMediaSizes[i] += getDirectorySize(imcs, path);
|
final UserEnvironment currentEnv = new UserEnvironment(currentUser);
|
||||||
}
|
|
||||||
} else {
|
// Measure media types for emulated storage, or for primary physical
|
||||||
// TODO Compute sizes using the MediaStore
|
// external volume
|
||||||
mMediaSizes[i] = 0;
|
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
|
// Measure misc files not counted under media
|
||||||
// Media sizes are measured by the MediaStore. Query database.
|
if (mIsInternal || mIsPrimary) {
|
||||||
ContentResolver contentResolver = context.getContentResolver();
|
final File path = mIsInternal ? currentEnv.getExternalStorageDirectory()
|
||||||
// TODO "external" as a static String from MediaStore?
|
: mVolume.getPathFile();
|
||||||
Uri audioUri = MediaStore.Files.getContentUri("external");
|
details.miscSize = measureMisc(imcs, path);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Misc
|
// Measure total emulated storage of all users; internal apps data
|
||||||
mMiscSize = 0;
|
// will be spliced in later
|
||||||
if (mIsPrimary) {
|
for (UserInfo user : users) {
|
||||||
measureSizesOfMisc(imcs);
|
final UserEnvironment userEnv = new UserEnvironment(user.id);
|
||||||
|
final long size = getDirectorySize(imcs, userEnv.getExternalStorageDirectory());
|
||||||
|
addValue(details.usersSize, user.id, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apps
|
// Measure all apps for all users
|
||||||
// We have to get installd to measure the package sizes.
|
final PackageManager pm = context.getPackageManager();
|
||||||
PackageManager pm = context.getPackageManager();
|
if (mIsInternal || mIsPrimary) {
|
||||||
if (pm == null) {
|
final List<ApplicationInfo> apps = pm.getInstalledApplications(
|
||||||
return;
|
PackageManager.GET_UNINSTALLED_PACKAGES
|
||||||
}
|
| PackageManager.GET_DISABLED_COMPONENTS);
|
||||||
final List<ApplicationInfo> 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (apps != null && apps.size() > 0) {
|
final int count = users.size() * apps.size();
|
||||||
// initiate measurement of all package sizes. need new StatsObserver object.
|
final StatsObserver observer = new StatsObserver(
|
||||||
mStatsObserver = new StatsObserver();
|
mIsInternal, details, currentUser, finished, count);
|
||||||
synchronized (mStatsObserver.mAppsList) {
|
|
||||||
for (int i = 0; i < apps.size(); i++) {
|
for (UserInfo user : users) {
|
||||||
final ApplicationInfo info = apps.get(i);
|
for (ApplicationInfo app : apps) {
|
||||||
mStatsObserver.queuePackageMeasurementLocked(info.packageName);
|
pm.getPackageSizeInfo(app.packageName, user.id, observer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestQueuedMeasurementsLocked(pm);
|
|
||||||
// Sending of the message back to the MeasurementReceiver is
|
|
||||||
// completed in the PackageObserver
|
|
||||||
} else {
|
} else {
|
||||||
onInternalMeasurementComplete();
|
finished.sendToTarget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getDirectorySize(IMediaContainerService imcs, String dir) {
|
private static long getDirectorySize(IMediaContainerService imcs, File path) {
|
||||||
try {
|
try {
|
||||||
return imcs.calculateDirectorySize(dir);
|
final long size = imcs.calculateDirectorySize(path.toString());
|
||||||
|
Log.d(TAG, "getDirectorySize(" + path + ") returned " + size);
|
||||||
|
return size;
|
||||||
} catch (Exception e) {
|
} 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;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long getMiscSize() {
|
private long measureMisc(IMediaContainerService imcs, File dir) {
|
||||||
return mMiscSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void measureSizesOfMisc(IMediaContainerService imcs) {
|
|
||||||
File top = new File(mStorageVolume.getPath());
|
|
||||||
mFileInfoForMisc = new ArrayList<FileInfo>();
|
mFileInfoForMisc = new ArrayList<FileInfo>();
|
||||||
File[] files = top.listFiles();
|
|
||||||
if (files == null) return;
|
final File[] files = dir.listFiles();
|
||||||
final int len = files.length;
|
if (files == null) return 0;
|
||||||
// Get sizes of all top level nodes except the ones already computed...
|
|
||||||
|
// Get sizes of all top level nodes except the ones already computed
|
||||||
long counter = 0;
|
long counter = 0;
|
||||||
for (int i = 0; i < len; i++) {
|
long miscSize = 0;
|
||||||
String path = files[i].getAbsolutePath();
|
|
||||||
if (StorageVolumePreferenceCategory.sPathsExcludedForMisc.contains(path)) {
|
for (File file : files) {
|
||||||
|
final String path = file.getAbsolutePath();
|
||||||
|
final String name = file.getName();
|
||||||
|
if (sMeasureMediaTypes.contains(name)) {
|
||||||
continue;
|
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++));
|
mFileInfoForMisc.add(new FileInfo(path, fileSize, counter++));
|
||||||
mMiscSize += fileSize;
|
miscSize += fileSize;
|
||||||
} else if (files[i].isDirectory()) {
|
} else if (file.isDirectory()) {
|
||||||
final long dirSize = getDirectorySize(imcs, path);
|
final long dirSize = getDirectorySize(imcs, file);
|
||||||
mFileInfoForMisc.add(new FileInfo(path, dirSize, counter++));
|
mFileInfoForMisc.add(new FileInfo(path, dirSize, counter++));
|
||||||
mMiscSize += dirSize;
|
miscSize += dirSize;
|
||||||
} else {
|
} else {
|
||||||
// Non directory, non file: not listed
|
// Non directory, non file: not listed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort the list of FileInfo objects collected above in descending order of their sizes
|
// sort the list of FileInfo objects collected above in descending order of their sizes
|
||||||
Collections.sort(mFileInfoForMisc);
|
Collections.sort(mFileInfoForMisc);
|
||||||
|
|
||||||
|
return miscSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class FileInfo implements Comparable<FileInfo> {
|
static class FileInfo implements Comparable<FileInfo> {
|
||||||
@@ -565,10 +516,7 @@ public class StorageMeasurement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static void addValue(SparseLongArray array, int key, long value) {
|
||||||
* TODO remove this method, only used because external SD Card needs a special treatment.
|
array.put(key, array.get(key) + value);
|
||||||
*/
|
|
||||||
boolean isExternalSDCard() {
|
|
||||||
return !mIsPrimary && !mIsInternal;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,13 +25,10 @@ import android.content.pm.IPackageManager;
|
|||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.hardware.usb.UsbManager;
|
import android.hardware.usb.UsbManager;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.Environment.UserEnvironment;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.os.storage.StorageManager;
|
import android.os.storage.StorageManager;
|
||||||
import android.os.storage.StorageVolume;
|
import android.os.storage.StorageVolume;
|
||||||
@@ -40,179 +37,98 @@ import android.preference.PreferenceCategory;
|
|||||||
import android.text.format.Formatter;
|
import android.text.format.Formatter;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.deviceinfo.StorageMeasurement.MeasurementDetails;
|
||||||
import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
|
import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
|
||||||
import com.google.android.collect.Lists;
|
import com.google.android.collect.Lists;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
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_USAGE_BAR = -2;
|
||||||
private static final int ORDER_STORAGE_LOW = -1;
|
private static final int ORDER_STORAGE_LOW = -1;
|
||||||
|
|
||||||
private Preference mItemTotal;
|
/** Physical volume being measured, or {@code null} for internal. */
|
||||||
private Preference mItemAvailable;
|
private final StorageVolume mVolume;
|
||||||
|
private final StorageMeasurement mMeasure;
|
||||||
|
|
||||||
|
private final Resources mResources;
|
||||||
|
private final StorageManager mStorageManager;
|
||||||
|
private final UserManager mUserManager;
|
||||||
|
|
||||||
private UsageBarPreference mUsageBarPreference;
|
private UsageBarPreference mUsageBarPreference;
|
||||||
private Preference mMountTogglePreference;
|
private Preference mMountTogglePreference;
|
||||||
private Preference mFormatPreference;
|
private Preference mFormatPreference;
|
||||||
private Preference mStorageLow;
|
private Preference mStorageLow;
|
||||||
|
|
||||||
private final StorageVolume mVolume;
|
private StorageItemPreference mItemTotal;
|
||||||
|
private StorageItemPreference mItemAvailable;
|
||||||
private final boolean mIsEmulated;
|
private StorageItemPreference mItemApps;
|
||||||
private final boolean mIsPrimary;
|
private StorageItemPreference mItemDcim;
|
||||||
|
private StorageItemPreference mItemMusic;
|
||||||
private final Resources mResources;
|
private StorageItemPreference mItemDownloads;
|
||||||
private final StorageManager mStorageManager;
|
private StorageItemPreference mItemMisc;
|
||||||
private final UserManager mUserManager;
|
private List<StorageItemPreference> mItemUsers = Lists.newArrayList();
|
||||||
|
|
||||||
@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<StorageMeasurement> mAllMeasures = Lists.newArrayList();
|
|
||||||
|
|
||||||
private boolean mAllowFormat;
|
|
||||||
|
|
||||||
private boolean mUsbConnected;
|
private boolean mUsbConnected;
|
||||||
private String mUsbFunction;
|
private String mUsbFunction;
|
||||||
private boolean mShowingApprox;
|
|
||||||
|
|
||||||
public static final Set<String> sPathsExcludedForMisc = new HashSet<String>();
|
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;
|
private static final int MSG_UI_UPDATE_APPROXIMATE = 1;
|
||||||
|
private static final int MSG_UI_UPDATE_DETAILS = 2;
|
||||||
// Updates the memory usage bar graph.
|
|
||||||
private static final int MSG_UI_UPDATE_EXACT = 2;
|
|
||||||
|
|
||||||
private Handler mUpdateHandler = new Handler() {
|
private Handler mUpdateHandler = new Handler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case MSG_UI_UPDATE_APPROXIMATE: {
|
case MSG_UI_UPDATE_APPROXIMATE: {
|
||||||
final UserHandle user = (UserHandle) msg.obj;
|
final long[] size = (long[]) msg.obj;
|
||||||
final Bundle bundle = msg.getData();
|
updateApproximate(size[0], size[1]);
|
||||||
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);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MSG_UI_UPDATE_EXACT: {
|
case MSG_UI_UPDATE_DETAILS: {
|
||||||
final UserHandle user = (UserHandle) msg.obj;
|
final MeasurementDetails details = (MeasurementDetails) msg.obj;
|
||||||
final Bundle bundle = msg.getData();
|
updateDetails(details);
|
||||||
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);
|
|
||||||
}
|
|
||||||
break;
|
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);
|
super(context);
|
||||||
|
|
||||||
mVolume = volume;
|
mVolume = volume;
|
||||||
|
mMeasure = StorageMeasurement.getInstance(context, volume);
|
||||||
mIsPrimary = volume != null ? volume.isPrimary() : false;
|
|
||||||
mIsEmulated = volume != null ? volume.isEmulated() : false;
|
|
||||||
|
|
||||||
mResources = context.getResources();
|
mResources = context.getResources();
|
||||||
mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
|
mStorageManager = StorageManager.from(context);
|
||||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||||
|
|
||||||
setTitle(volume != null ? volume.getDescription(context)
|
setTitle(volume != null ? volume.getDescription(context)
|
||||||
: context.getText(R.string.internal_storage));
|
: 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) {
|
private StorageItemPreference buildItem(int titleRes, int colorRes) {
|
||||||
final StorageItemPreference pref = new StorageItemPreference(
|
return new StorageItemPreference(getContext(), titleRes, colorRes);
|
||||||
getContext(), key, titleRes, colorRes);
|
|
||||||
addPreference(pref);
|
|
||||||
return pref;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String buildUserKey(UserHandle user) {
|
|
||||||
return KEY_USER_PREFIX + user.getIdentifier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
@@ -226,51 +142,62 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
|
|||||||
}
|
}
|
||||||
|
|
||||||
final List<UserInfo> otherUsers = getUsersExcluding(currentUser);
|
final List<UserInfo> otherUsers = getUsersExcluding(currentUser);
|
||||||
final boolean measureUsers = mIsEmulated && mIsPrimary && otherUsers.size() > 0;
|
final boolean showUsers = mVolume == null && otherUsers.size() > 0;
|
||||||
|
|
||||||
mUsageBarPreference = new UsageBarPreference(context);
|
mUsageBarPreference = new UsageBarPreference(context);
|
||||||
mUsageBarPreference.setOrder(ORDER_USAGE_BAR);
|
mUsageBarPreference.setOrder(ORDER_USAGE_BAR);
|
||||||
addPreference(mUsageBarPreference);
|
addPreference(mUsageBarPreference);
|
||||||
|
|
||||||
mItemTotal = addStorageItem(KEY_TOTAL_SIZE, R.string.memory_size, 0);
|
mItemTotal = buildItem(R.string.memory_size, 0);
|
||||||
mItemAvailable = addStorageItem(
|
mItemAvailable = buildItem(R.string.memory_available, R.color.memory_avail);
|
||||||
KEY_AVAILABLE, R.string.memory_available, R.color.memory_avail);
|
addPreference(mItemTotal);
|
||||||
|
addPreference(mItemAvailable);
|
||||||
|
|
||||||
if (measureUsers) {
|
mItemApps = buildItem(R.string.memory_apps_usage, R.color.memory_apps_usage);
|
||||||
addPreference(new PreferenceHeader(context, currentUser.name));
|
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);
|
final boolean showDetails = mVolume == null || mVolume.isPrimary();
|
||||||
addStorageItem(KEY_DCIM, R.string.memory_dcim_usage, R.color.memory_dcim);
|
if (showDetails) {
|
||||||
addStorageItem(KEY_MUSIC, R.string.memory_music_usage, R.color.memory_music);
|
if (showUsers) {
|
||||||
addStorageItem(KEY_DOWNLOADS, R.string.memory_downloads_usage, R.color.memory_downloads);
|
addPreference(new PreferenceHeader(context, currentUser.name));
|
||||||
addStorageItem(KEY_MISC, R.string.memory_media_misc_usage, R.color.memory_misc);
|
}
|
||||||
|
|
||||||
if (measureUsers) {
|
addPreference(mItemApps);
|
||||||
addPreference(new PreferenceHeader(context, R.string.storage_other_users));
|
addPreference(mItemDcim);
|
||||||
|
addPreference(mItemMusic);
|
||||||
|
addPreference(mItemDownloads);
|
||||||
|
addPreference(mItemMisc);
|
||||||
|
|
||||||
int count = 0;
|
if (showUsers) {
|
||||||
for (UserInfo info : otherUsers) {
|
addPreference(new PreferenceHeader(context, R.string.storage_other_users));
|
||||||
final UserHandle user = new UserHandle(info.id);
|
|
||||||
final String key = buildUserKey(user);
|
|
||||||
|
|
||||||
final StorageMeasurement measure = StorageMeasurement.getInstance(
|
int count = 0;
|
||||||
context, mVolume, user);
|
for (UserInfo info : otherUsers) {
|
||||||
measure.setIncludeAppCodeSize(false);
|
final int colorRes = count++ % 2 == 0 ? R.color.memory_user_light
|
||||||
mAllMeasures.add(measure);
|
: R.color.memory_user_dark;
|
||||||
|
final StorageItemPreference userPref = new StorageItemPreference(
|
||||||
final int colorRes = count++ % 2 == 0 ? R.color.memory_user_light
|
getContext(), info.name, colorRes, info.id);
|
||||||
: R.color.memory_user_dark;
|
mItemUsers.add(userPref);
|
||||||
addPreference(new StorageItemPreference(getContext(), key, info.name, colorRes));
|
addPreference(userPref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mMountTogglePreference = new Preference(context);
|
final boolean isRemovable = mVolume != null ? mVolume.isRemovable() : false;
|
||||||
mMountTogglePreference.setTitle(R.string.sd_eject);
|
if (isRemovable) {
|
||||||
mMountTogglePreference.setSummary(R.string.sd_eject_summary);
|
mMountTogglePreference = new Preference(context);
|
||||||
addPreference(mMountTogglePreference);
|
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 = new Preference(context);
|
||||||
mFormatPreference.setTitle(R.string.sd_format);
|
mFormatPreference.setTitle(R.string.sd_format);
|
||||||
mFormatPreference.setSummary(R.string.sd_format_summary);
|
mFormatPreference.setSummary(R.string.sd_format_summary);
|
||||||
@@ -298,33 +225,24 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updatePreferencesFromState() {
|
private void updatePreferencesFromState() {
|
||||||
|
// Only update for physical volumes
|
||||||
|
if (mVolume == null) return;
|
||||||
|
|
||||||
mMountTogglePreference.setEnabled(true);
|
mMountTogglePreference.setEnabled(true);
|
||||||
|
|
||||||
String state = mVolume != null
|
final String state = mStorageManager.getVolumeState(mVolume.getPath());
|
||||||
? mStorageManager.getVolumeState(mVolume.getPath())
|
|
||||||
: Environment.MEDIA_MOUNTED;
|
|
||||||
|
|
||||||
String readOnly = "";
|
|
||||||
if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
|
if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
|
||||||
state = Environment.MEDIA_MOUNTED;
|
mItemAvailable.setSummary(R.string.memory_available_read_only);
|
||||||
readOnly = mResources.getString(R.string.read_only);
|
|
||||||
if (mFormatPreference != null) {
|
if (mFormatPreference != null) {
|
||||||
removePreference(mFormatPreference);
|
removePreference(mFormatPreference);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mItemAvailable.setSummary(R.string.memory_available);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((mVolume == null || !mVolume.isRemovable())
|
if (Environment.MEDIA_MOUNTED.equals(state)
|
||||||
&& !Environment.MEDIA_UNMOUNTED.equals(state)) {
|
|| Environment.MEDIA_MOUNTED_READ_ONLY.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);
|
|
||||||
|
|
||||||
mMountTogglePreference.setEnabled(true);
|
mMountTogglePreference.setEnabled(true);
|
||||||
mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
|
mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
|
||||||
mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary));
|
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) ||
|
if (mUsbConnected && (UsbManager.USB_FUNCTION_MTP.equals(mUsbFunction) ||
|
||||||
UsbManager.USB_FUNCTION_PTP.equals(mUsbFunction))) {
|
UsbManager.USB_FUNCTION_PTP.equals(mUsbFunction))) {
|
||||||
mMountTogglePreference.setEnabled(false);
|
mMountTogglePreference.setEnabled(false);
|
||||||
if (Environment.MEDIA_MOUNTED.equals(state)) {
|
if (Environment.MEDIA_MOUNTED.equals(state)
|
||||||
mMountTogglePreference.setSummary(mResources.getString(R.string.mtp_ptp_mode_summary));
|
|| Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
|
||||||
|
mMountTogglePreference.setSummary(
|
||||||
|
mResources.getString(R.string.mtp_ptp_mode_summary));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mFormatPreference != null) {
|
if (mFormatPreference != null) {
|
||||||
@@ -363,99 +283,78 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
|
|||||||
mFormatPreference.setEnabled(true);
|
mFormatPreference.setEnabled(true);
|
||||||
mFormatPreference.setSummary(mResources.getString(R.string.sd_format_summary));
|
mFormatPreference.setSummary(mResources.getString(R.string.sd_format_summary));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateApproximate(long totalSize, long availSize) {
|
public void updateApproximate(long totalSize, long availSize) {
|
||||||
mItemTotal.setSummary(formatSize(totalSize));
|
mItemTotal.setSummary(formatSize(totalSize));
|
||||||
mItemAvailable.setSummary(formatSize(availSize));
|
mItemAvailable.setSummary(formatSize(availSize));
|
||||||
|
|
||||||
|
mTotalSize = totalSize;
|
||||||
|
|
||||||
final long usedSize = totalSize - availSize;
|
final long usedSize = totalSize - availSize;
|
||||||
|
|
||||||
mUsageBarPreference.clear();
|
mUsageBarPreference.clear();
|
||||||
mUsageBarPreference.addEntry(0, usedSize / (float) totalSize, android.graphics.Color.GRAY);
|
mUsageBarPreference.addEntry(0, usedSize / (float) totalSize, android.graphics.Color.GRAY);
|
||||||
mUsageBarPreference.commit();
|
mUsageBarPreference.commit();
|
||||||
mShowingApprox = true;
|
|
||||||
|
|
||||||
updatePreferencesFromState();
|
updatePreferencesFromState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateExact(long totalSize, long availSize, long appsSize, long downloadsSize,
|
private static long totalValues(HashMap<String, Long> map, String... keys) {
|
||||||
long miscSize, long[] mediaSizes) {
|
long total = 0;
|
||||||
if (mShowingApprox) {
|
for (String key : keys) {
|
||||||
mUsageBarPreference.clear();
|
total += map.get(key);
|
||||||
mShowingApprox = false;
|
|
||||||
}
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
mItemTotal.setSummary(formatSize(totalSize));
|
public void updateDetails(MeasurementDetails details) {
|
||||||
|
final boolean showDetails = mVolume == null || mVolume.isPrimary();
|
||||||
|
if (!showDetails) return;
|
||||||
|
|
||||||
if (mLocalMeasure.isExternalSDCard()) {
|
mUsageBarPreference.clear();
|
||||||
// TODO FIXME: external SD card will not report any size. Show used space in bar graph
|
|
||||||
final long usedSize = totalSize - availSize;
|
updatePreference(mItemApps, details.appsSize);
|
||||||
mUsageBarPreference.addEntry(
|
|
||||||
0, usedSize / (float) totalSize, android.graphics.Color.GRAY);
|
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();
|
mUsageBarPreference.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateUserExact(UserHandle user, long totalSize, long usedSize) {
|
private void updatePreference(StorageItemPreference pref, long size) {
|
||||||
if (mShowingApprox) {
|
if (size > 0) {
|
||||||
mUsageBarPreference.clear();
|
pref.setSummary(formatSize(size));
|
||||||
mShowingApprox = false;
|
final int order = pref.getOrder();
|
||||||
}
|
mUsageBarPreference.addEntry(order, size / (float) mTotalSize, pref.color);
|
||||||
|
} else {
|
||||||
final String key = buildUserKey(user);
|
removePreference(pref);
|
||||||
|
|
||||||
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 measure() {
|
private void measure() {
|
||||||
for (StorageMeasurement measure : mAllMeasures) {
|
mMeasure.invalidate();
|
||||||
measure.invalidate();
|
mMeasure.measure();
|
||||||
measure.measure();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
for (StorageMeasurement measure : mAllMeasures) {
|
mMeasure.setReceiver(mReceiver);
|
||||||
measure.setReceiver(this);
|
|
||||||
}
|
|
||||||
measure();
|
measure();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,30 +373,25 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
for (StorageMeasurement measure : mAllMeasures) {
|
mMeasure.cleanUp();
|
||||||
measure.cleanUp();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String formatSize(long size) {
|
private String formatSize(long size) {
|
||||||
return Formatter.formatFileSize(getContext(), size);
|
return Formatter.formatFileSize(getContext(), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private MeasurementReceiver mReceiver = new MeasurementReceiver() {
|
||||||
public void updateApproximate(StorageMeasurement meas, Bundle bundle) {
|
@Override
|
||||||
final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE);
|
public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize) {
|
||||||
message.obj = meas.getUser();
|
mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE, new long[] {
|
||||||
message.setData(bundle);
|
totalSize, availSize }).sendToTarget();
|
||||||
mUpdateHandler.sendMessage(message);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateExact(StorageMeasurement meas, Bundle bundle) {
|
public void updateDetails(StorageMeasurement meas, MeasurementDetails details) {
|
||||||
final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXACT);
|
mUpdateHandler.obtainMessage(MSG_UI_UPDATE_DETAILS, details).sendToTarget();
|
||||||
message.obj = meas.getUser();
|
}
|
||||||
message.setData(bundle);
|
};
|
||||||
mUpdateHandler.sendMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean mountToggleClicked(Preference preference) {
|
public boolean mountToggleClicked(Preference preference) {
|
||||||
return preference == mMountTogglePreference;
|
return preference == mMountTogglePreference;
|
||||||
@@ -514,27 +408,25 @@ public class StorageVolumePreferenceCategory extends PreferenceCategory
|
|||||||
intent = new Intent(Intent.ACTION_VIEW);
|
intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setClass(getContext(), com.android.settings.MediaFormat.class);
|
intent.setClass(getContext(), com.android.settings.MediaFormat.class);
|
||||||
intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
|
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 = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
|
||||||
intent.setClass(getContext(),
|
intent.setClass(getContext(),
|
||||||
com.android.settings.Settings.ManageApplicationsActivity.class);
|
com.android.settings.Settings.ManageApplicationsActivity.class);
|
||||||
} else if (KEY_DOWNLOADS.equals(key)) {
|
} else if (pref == mItemDownloads) {
|
||||||
intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra(
|
intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra(
|
||||||
DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
|
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 = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
intent.setType("audio/mp3");
|
intent.setType("audio/mp3");
|
||||||
} else if (KEY_DCIM.equals(key)) {
|
} else if (pref == mItemDcim) {
|
||||||
intent = new Intent(Intent.ACTION_VIEW);
|
intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
|
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
|
||||||
// TODO Create a Videos category, type = vnd.android.cursor.dir/video
|
// TODO Create a Videos category, type = vnd.android.cursor.dir/video
|
||||||
intent.setType("vnd.android.cursor.dir/image");
|
intent.setType("vnd.android.cursor.dir/image");
|
||||||
} else if (KEY_MISC.equals(key)) {
|
} else if (pref == mItemMisc) {
|
||||||
Context context = getContext().getApplicationContext();
|
Context context = getContext().getApplicationContext();
|
||||||
if (mLocalMeasure.getMiscSize() > 0) {
|
intent = new Intent(context, MiscFilesHandler.class);
|
||||||
intent = new Intent(context, MiscFilesHandler.class);
|
intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
|
||||||
intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return intent;
|
return intent;
|
||||||
|
Reference in New Issue
Block a user