Merge "Iteration on multi-user Storage UI." into jb-mr1-dev
This commit is contained in:
@@ -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<StorageVolumePreferenceCategory> 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();
|
||||
|
||||
@@ -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<StorageMeasurement.FileInfo>) mMeasurement.mFileInfoForMisc;
|
||||
if (mData != null) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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>
|
||||
sInstances = Maps.newHashMap();
|
||||
@GuardedBy("sInstances")
|
||||
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;
|
||||
|
||||
/** 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<FileInfo> 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<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) {
|
||||
if (mReceiver == null || mReceiver.get() == null) {
|
||||
mReceiver = new WeakReference<MeasurementReceiver>(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<Context> 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<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) {
|
||||
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<UserInfo> 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<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();
|
||||
}
|
||||
// Measure all apps for all users
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
if (mIsInternal || mIsPrimary) {
|
||||
final List<ApplicationInfo> 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<FileInfo>();
|
||||
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<FileInfo> {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<StorageMeasurement> 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<StorageItemPreference> mItemUsers = Lists.newArrayList();
|
||||
|
||||
private boolean mUsbConnected;
|
||||
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;
|
||||
|
||||
// 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<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.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<String, Long> 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;
|
||||
|
||||
Reference in New Issue
Block a user