diff options
author | Kenny Root <kroot@google.com> | 2010-12-13 16:23:57 -0800 |
---|---|---|
committer | Kenny Root <kroot@google.com> | 2010-12-17 09:32:45 -0800 |
commit | e4330890d6996bd1ad65bc6c5ab1dfb39f224ea4 (patch) | |
tree | fdd7c755e1472508723203ab267f1c12a9d248be /src/com | |
parent | c88a7ff1efd10374974e45768bde1658cc1d8483 (diff) | |
download | packages_apps_Settings-e4330890d6996bd1ad65bc6c5ab1dfb39f224ea4.tar.gz packages_apps_Settings-e4330890d6996bd1ad65bc6c5ab1dfb39f224ea4.tar.bz2 packages_apps_Settings-e4330890d6996bd1ad65bc6c5ab1dfb39f224ea4.zip |
Refactor memory measurement
Move out all the memory measurement to a separate class that can cache
all of its data through orientation changes.
Tweak the display to make it draw large squares for the legend instead
of a 1x1 square. Add padding to the percentage bar chart.
Change-Id: I4cd5bc4ba31a422a55740e8f58e5e571cf9866a5
Diffstat (limited to 'src/com')
3 files changed, 598 insertions, 276 deletions
diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java index 4ef08d134..fb3728f3a 100644 --- a/src/com/android/settings/deviceinfo/Memory.java +++ b/src/com/android/settings/deviceinfo/Memory.java @@ -16,38 +16,32 @@ package com.android.settings.deviceinfo; -import com.android.internal.app.IMediaContainerService; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.deviceinfo.MemoryMeasurement.MeasurementReceiver; -import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; import android.app.Dialog; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; -import android.content.ServiceConnection; import android.content.DialogInterface.OnCancelListener; import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageStatsObserver; -import android.content.pm.PackageManager; -import android.content.pm.PackageStats; import android.content.res.Resources; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RectShape; +import android.graphics.drawable.shapes.RoundRectShape; import android.hardware.UsbManager; import android.os.Bundle; import android.os.Environment; import android.os.Handler; -import android.os.HandlerThread; import android.os.IBinder; -import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.StatFs; import android.os.storage.IMountService; import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; @@ -58,11 +52,10 @@ import android.text.format.Formatter; import android.util.Log; import android.widget.Toast; -import java.io.File; -import java.util.ArrayList; import java.util.List; -public class Memory extends SettingsPreferenceFragment implements OnCancelListener { +public class Memory extends SettingsPreferenceFragment implements OnCancelListener, + MeasurementReceiver { private static final String TAG = "Memory"; private static final boolean localLOGV = false; @@ -110,13 +103,6 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen private int mInternalAppsColor; private int mInternalUsedColor; - // Internal memory fields - private long mInternalTotalSize; - private long mInternalUsedSize; - private long mInternalMediaSize; - private long mInternalAppsSize; - private boolean mMeasured = false; - boolean mSdMountToggleAdded = true; // Access using getMountService() @@ -125,241 +111,46 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen private StorageManager mStorageManager = null; // Updates the memory usage bar graph. - private static final int MSG_UI_UPDATE_APPROXIMATE = 1; + private static final int MSG_UI_UPDATE_INTERNAL_APPROXIMATE = 1; // Updates the memory usage bar graph. - private static final int MSG_UI_UPDATE_EXACT = 2; - - private Handler mUpdateHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_UI_UPDATE_APPROXIMATE: - updateUiApproximate(); - break; - case MSG_UI_UPDATE_EXACT: - updateUiExact(); - mMeasured = true; - break; - } - } - }; - - private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer"; - - private static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( - DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService"); - - class MemoryMeasurementHandler extends Handler { - public static final int MSG_MEASURE_ALL = 1; - - public static final int MSG_CONNECTED = 2; - - public static final int MSG_DISCONNECTED = 3; + private static final int MSG_UI_UPDATE_INTERNAL_EXACT = 2; - private List<String> mPendingApps = new ArrayList<String>(); - - private volatile boolean mBound = false; - - private long mAppsSize = 0; - - final private ServiceConnection mDefContainerConn = new ServiceConnection() { - public void onServiceConnected(ComponentName name, IBinder service) { - mBound = true; - IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service); - mMeasurementHandler.sendMessage(mMeasurementHandler.obtainMessage( - MemoryMeasurementHandler.MSG_CONNECTED, imcs)); - } - - public void onServiceDisconnected(ComponentName name) { - mBound = false; - } - }; - - MemoryMeasurementHandler(Looper looper) { - super(looper); - } + // Updates the memory usage stats for external. + private static final int MSG_UI_UPDATE_EXTERNAL_APPROXIMATE = 3; + private Handler mUpdateHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_MEASURE_ALL: { - updateExternalStorage(); - updateApproximateInternalStorage(); - - Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); - getActivity().bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE); - - mUpdateHandler.sendEmptyMessage(MSG_UI_UPDATE_APPROXIMATE); + case MSG_UI_UPDATE_INTERNAL_APPROXIMATE: { + Bundle bundle = msg.getData(); + final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE); + final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE); + updateUiApproximate(totalSize, availSize); break; } - case MSG_CONNECTED: { - IMediaContainerService imcs = (IMediaContainerService) msg.obj; - updateExactInternalStorage(imcs); - } - } - } - - public void cleanUp() { - if (mBound) { - getActivity().unbindService(mDefContainerConn); - } - } - - public void queuePackageMeasurementLocked(String packageName) { - mPendingApps.add(packageName); - } - - public void requestQueuedMeasurementsLocked() { - final Activity activity = getActivity(); - if (activity == null) { - return; - } - - final PackageManager pm = activity.getPackageManager(); - if (pm == null) { - return; - } - - final int N = mPendingApps.size(); - for (int i = 0; i < N; i++) { - pm.getPackageSizeInfo(mPendingApps.get(i), mStatsObserver); - } - } - - final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() { - public void onGetStatsCompleted(PackageStats stats, boolean succeeded) { - if (succeeded) { - mAppsSize += stats.codeSize + stats.dataSize; - } - - synchronized (mPendingApps) { - mPendingApps.remove(stats.packageName); - - if (mPendingApps.size() == 0) { - mInternalAppsSize = mAppsSize; - - mUpdateHandler.sendEmptyMessage(MSG_UI_UPDATE_EXACT); - } - } - } - }; - - private void updateApproximateInternalStorage() { - final File dataPath = Environment.getDataDirectory(); - final StatFs stat = new StatFs(dataPath.getPath()); - final long blockSize = stat.getBlockSize(); - final long totalBlocks = stat.getBlockCount(); - final long availableBlocks = stat.getAvailableBlocks(); - - final long totalSize = totalBlocks * blockSize; - final long availSize = availableBlocks * blockSize; - mInternalSize.setSummary(formatSize(totalSize)); - mInternalAvail.setSummary(formatSize(availSize)); - - mInternalTotalSize = totalSize; - mInternalUsedSize = totalSize - availSize; - } - - private void updateExactInternalStorage(IMediaContainerService imcs) { - long mediaSize; - try { - // TODO get these directories from somewhere - mediaSize = imcs.calculateDirectorySize("/data/media"); - } catch (Exception e) { - Log.i(TAG, "Could not read memory from default container service"); - return; - } - - mInternalMediaSize = mediaSize; - - // We have to get installd to measure the package sizes. - PackageManager pm = getPackageManager(); - List<ApplicationInfo> apps = pm - .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES - | PackageManager.GET_DISABLED_COMPONENTS); - if (apps != null) { - synchronized (mPendingApps) { - for (int i = 0; i < apps.size(); i++) { - final ApplicationInfo info = apps.get(i); - queuePackageMeasurementLocked(info.packageName); - } - - requestQueuedMeasurementsLocked(); - } - } - } - - private void updateExternalStorage() { - if (Environment.isExternalStorageEmulated()) { - return; - } - - String status = Environment.getExternalStorageState(); - String readOnly = ""; - if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { - status = Environment.MEDIA_MOUNTED; - readOnly = mRes.getString(R.string.read_only); - } - - if (status.equals(Environment.MEDIA_MOUNTED)) { - if (!Environment.isExternalStorageRemovable()) { - // This device has built-in storage that is not removable. - // There is no reason for the user to unmount it. - if (mSdMountToggleAdded) { - mSdMountPreferenceGroup.removePreference(mSdMountToggle); - mSdMountToggleAdded = false; - } - } - try { - File path = Environment.getExternalStorageDirectory(); - StatFs stat = new StatFs(path.getPath()); - long blockSize = stat.getBlockSize(); - long totalBlocks = stat.getBlockCount(); - long availableBlocks = stat.getAvailableBlocks(); - - mSdSize.setSummary(formatSize(totalBlocks * blockSize)); - mSdAvail.setSummary(formatSize(availableBlocks * blockSize) + readOnly); - - mSdMountToggle.setEnabled(true); - mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject)); - mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary)); - - } catch (IllegalArgumentException e) { - // this can occur if the SD card is removed, but we haven't - // received the - // ACTION_MEDIA_REMOVED Intent yet. - status = Environment.MEDIA_REMOVED; - } - } else { - mSdSize.setSummary(mRes.getString(R.string.sd_unavailable)); - mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable)); - - if (!Environment.isExternalStorageRemovable()) { - if (status.equals(Environment.MEDIA_UNMOUNTED)) { - if (!mSdMountToggleAdded) { - mSdMountPreferenceGroup.addPreference(mSdMountToggle); - mSdMountToggleAdded = true; - } - } + case MSG_UI_UPDATE_INTERNAL_EXACT: { + Bundle bundle = msg.getData(); + final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE); + final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE); + final long mediaUsed = bundle.getLong(MemoryMeasurement.MEDIA_USED); + final long appsUsed = bundle.getLong(MemoryMeasurement.APPS_USED); + updateUiExact(totalSize, availSize, mediaUsed, appsUsed); + break; } - - if (status.equals(Environment.MEDIA_UNMOUNTED) || - status.equals(Environment.MEDIA_NOFS) || - status.equals(Environment.MEDIA_UNMOUNTABLE) ) { - mSdMountToggle.setEnabled(true); - mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount)); - mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary)); - } else { - mSdMountToggle.setEnabled(false); - mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount)); - mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary)); + case MSG_UI_UPDATE_EXTERNAL_APPROXIMATE: { + Bundle bundle = msg.getData(); + final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE); + final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE); + updateExternalStorage(totalSize, availSize); + break; } } } - } + }; - private MemoryMeasurementHandler mMeasurementHandler; + private MemoryMeasurement mMeasurement; @Override public void onCreate(Bundle icicle) { @@ -394,12 +185,27 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen mInternalAppsColor = mRes.getColor(R.color.memory_apps_usage); mInternalUsedColor = mRes.getColor(R.color.memory_used); + float[] radius = new float[] { + 5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f + }; + RoundRectShape shape1 = new RoundRectShape(radius, null, null); + + ShapeDrawable mediaShape = new ShapeDrawable(shape1); + mediaShape.setIntrinsicWidth(32); + mediaShape.setIntrinsicHeight(32); + mediaShape.getPaint().setColor(mInternalMediaColor); + mInternalMediaUsage.setIcon(mediaShape); + + ShapeDrawable appsShape = new ShapeDrawable(shape1); + appsShape.setIntrinsicWidth(32); + appsShape.setIntrinsicHeight(32); + appsShape.getPaint().setColor(mInternalAppsColor); + mInternalAppsUsage.setIcon(appsShape); + mInternalUsageChart = (UsageBarPreference) findPreference(MEMORY_INTERNAL_CHART); - // Start the thread that will measure the disk usage. - final HandlerThread t = new HandlerThread("MeasurementHandler"); - t.start(); - mMeasurementHandler = new MemoryMeasurementHandler(t.getLooper()); + mMeasurement = MemoryMeasurement.getInstance(getActivity()); + mMeasurement.setReceiver(this); } @Override @@ -411,9 +217,10 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen intentFilter.addDataScheme("file"); getActivity().registerReceiver(mReceiver, intentFilter); - if (!mMeasured) { - mMeasurementHandler.sendEmptyMessage(MemoryMeasurementHandler.MSG_MEASURE_ALL); + if (!Environment.isExternalStorageEmulated()) { + mMeasurement.measureExternal(); } + mMeasurement.measureInternal(); } StorageEventListener mStorageListener = new StorageEventListener() { @@ -422,7 +229,9 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen Log.i(TAG, "Received storage state changed notification that " + path + " changed state from " + oldState + " to " + newState); - mMeasurementHandler.sendEmptyMessage(MemoryMeasurementHandler.MSG_MEASURE_ALL); + if (!Environment.isExternalStorageEmulated()) { + mMeasurement.measureExternal(); + } } }; @@ -430,7 +239,7 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen public void onPause() { super.onPause(); getActivity().unregisterReceiver(mReceiver); - mMeasurementHandler.removeMessages(MemoryMeasurementHandler.MSG_MEASURE_ALL); + mMeasurement.cleanUp(); } @Override @@ -438,7 +247,6 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen if (mStorageManager != null && mStorageListener != null) { mStorageManager.unregisterListener(mStorageListener); } - mMeasurementHandler.cleanUp(); super.onDestroy(); } @@ -477,7 +285,12 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - mMeasurementHandler.sendEmptyMessage(MemoryMeasurementHandler.MSG_MEASURE_ALL); + mMeasurement.invalidate(); + + if (!Environment.isExternalStorageEmulated()) { + mMeasurement.measureExternal(); + } + mMeasurement.measureInternal(); } }; @@ -572,35 +385,95 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen } } - private void updateUiExact() { - final float totalSize = mInternalTotalSize; - - final long mediaSize = mInternalMediaSize; - final long appsSize = mInternalAppsSize; + private void updateUiExact(long totalSize, long availSize, long mediaSize, long appsSize) { + mInternalSize.setSummary(formatSize(totalSize)); + mInternalAvail.setSummary(formatSize(availSize)); + mInternalMediaUsage.setSummary(formatSize(mediaSize)); + mInternalAppsUsage.setSummary(formatSize(appsSize)); mInternalUsageChart.clear(); - mInternalUsageChart.addEntry(mediaSize / totalSize, mInternalMediaColor); - mInternalUsageChart.addEntry(appsSize / totalSize, mInternalAppsColor); + mInternalUsageChart.addEntry(mediaSize / (float) totalSize, mInternalMediaColor); + mInternalUsageChart.addEntry(appsSize / (float) totalSize, mInternalAppsColor); + + final long usedSize = totalSize - availSize; // There are other things that can take up storage, but we didn't // measure it. - final long remaining = mInternalUsedSize - (mediaSize + appsSize); + final long remaining = usedSize - (mediaSize + appsSize); if (remaining > 0) { - mInternalUsageChart.addEntry(remaining / totalSize, mInternalUsedColor); + mInternalUsageChart.addEntry(remaining / (float) totalSize, mInternalUsedColor); } mInternalUsageChart.commit(); - - mInternalMediaUsage.setSummary(formatSize(mediaSize)); - mInternalAppsUsage.setSummary(formatSize(appsSize)); } - private void updateUiApproximate() { + private void updateUiApproximate(long totalSize, long availSize) { + mInternalSize.setSummary(formatSize(totalSize)); + mInternalAvail.setSummary(formatSize(availSize)); + + final long usedSize = totalSize - availSize; + mInternalUsageChart.clear(); - mInternalUsageChart.addEntry(mInternalUsedSize / (float) mInternalTotalSize, getResources() - .getColor(R.color.memory_used)); + mInternalUsageChart.addEntry(usedSize / (float) totalSize, mInternalUsedColor); mInternalUsageChart.commit(); } + private void updateExternalStorage(long totalSize, long availSize) { + String status = Environment.getExternalStorageState(); + String readOnly = ""; + if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { + status = Environment.MEDIA_MOUNTED; + readOnly = mRes.getString(R.string.read_only); + } + + if (status.equals(Environment.MEDIA_MOUNTED)) { + if (!Environment.isExternalStorageRemovable()) { + // This device has built-in storage that is not removable. + // There is no reason for the user to unmount it. + if (mSdMountToggleAdded) { + mSdMountPreferenceGroup.removePreference(mSdMountToggle); + mSdMountToggleAdded = false; + } + } + try { + mSdSize.setSummary(formatSize(totalSize)); + mSdAvail.setSummary(formatSize(availSize) + readOnly); + + mSdMountToggle.setEnabled(true); + mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject)); + mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary)); + + } catch (IllegalArgumentException e) { + // this can occur if the SD card is removed, but we haven't + // received the + // ACTION_MEDIA_REMOVED Intent yet. + status = Environment.MEDIA_REMOVED; + } + } else { + mSdSize.setSummary(mRes.getString(R.string.sd_unavailable)); + mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable)); + + if (!Environment.isExternalStorageRemovable()) { + if (status.equals(Environment.MEDIA_UNMOUNTED)) { + if (!mSdMountToggleAdded) { + mSdMountPreferenceGroup.addPreference(mSdMountToggle); + mSdMountToggleAdded = true; + } + } + } + + if (status.equals(Environment.MEDIA_UNMOUNTED) || status.equals(Environment.MEDIA_NOFS) + || status.equals(Environment.MEDIA_UNMOUNTABLE)) { + mSdMountToggle.setEnabled(true); + mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount)); + mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary)); + } else { + mSdMountToggle.setEnabled(false); + mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount)); + mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary)); + } + } + } + private String formatSize(long size) { return Formatter.formatFileSize(getActivity(), size); } @@ -609,4 +482,25 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen // TODO: Is this really required? // finish(); } + + @Override + public void updateApproximateExternal(Bundle bundle) { + final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXTERNAL_APPROXIMATE); + message.setData(bundle); + mUpdateHandler.sendMessage(message); + } + + @Override + public void updateApproximateInternal(Bundle bundle) { + final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_APPROXIMATE); + message.setData(bundle); + mUpdateHandler.sendMessage(message); + } + + @Override + public void updateExactInternal(Bundle bundle) { + final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_INTERNAL_EXACT); + message.setData(bundle); + mUpdateHandler.sendMessage(message); + } } diff --git a/src/com/android/settings/deviceinfo/MemoryMeasurement.java b/src/com/android/settings/deviceinfo/MemoryMeasurement.java new file mode 100644 index 000000000..ead9dd6ec --- /dev/null +++ b/src/com/android/settings/deviceinfo/MemoryMeasurement.java @@ -0,0 +1,403 @@ +package com.android.settings.deviceinfo; + +import com.android.internal.app.IMediaContainerService; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +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.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.StatFs; +import android.util.Log; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +/** + * 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 StatFs) + * Directory measurements (using DefaultContainerService.measureDir) + * Applicaiton 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 MemoryMeasurement { + private static final String TAG = "MemoryMeasurement"; + + 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 MEDIA_USED = "media_used"; + + private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer"; + + private static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( + DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService"); + + private final MeasurementHandler mHandler; + + private static volatile MemoryMeasurement sInstance; + + private volatile WeakReference<MeasurementReceiver> mReceiver; + + // Internal memory fields + private long mInternalTotalSize; + private long mInternalAvailSize; + private long mInternalMediaSize; + private long mInternalAppsSize; + + // External memory fields + private long mExternalTotalSize; + + private long mExternalAvailSize; + + private MemoryMeasurement(Context context) { + // Start the thread that will measure the disk usage. + final HandlerThread t = new HandlerThread("MemoryMeasurement"); + t.start(); + mHandler = new MeasurementHandler(context, t.getLooper()); + } + + /** + * Get the singleton of the MemoryMeasurement class. The application + * context is used to avoid leaking activities. + */ + public static MemoryMeasurement getInstance(Context context) { + if (sInstance == null) { + synchronized (MemoryMeasurement.class) { + if (sInstance == null) { + sInstance = new MemoryMeasurement(context.getApplicationContext()); + } + } + } + + return sInstance; + } + + public void setReceiver(MeasurementReceiver receiver) { + mReceiver = new WeakReference<MeasurementReceiver>(receiver); + } + + public void measureExternal() { + if (!mHandler.hasMessages(MeasurementHandler.MSG_MEASURE_EXTERNAL)) { + mHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE_EXTERNAL); + } + } + + public void measureInternal() { + if (!mHandler.hasMessages(MeasurementHandler.MSG_MEASURE_INTERNAL)) { + mHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE_INTERNAL); + } + } + + public void cleanUp() { + mReceiver = null; + mHandler.cleanUp(); + } + + private void sendInternalApproximateUpdate() { + MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null; + if (receiver == null) { + return; + } + + Bundle bundle = new Bundle(); + bundle.putLong(TOTAL_SIZE, mInternalTotalSize); + bundle.putLong(AVAIL_SIZE, mInternalAvailSize); + + receiver.updateApproximateInternal(bundle); + } + + private void sendInternalExactUpdate() { + MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null; + if (receiver == null) { + return; + } + + Bundle bundle = new Bundle(); + bundle.putLong(TOTAL_SIZE, mInternalTotalSize); + bundle.putLong(AVAIL_SIZE, mInternalAvailSize); + bundle.putLong(APPS_USED, mInternalAppsSize); + bundle.putLong(MEDIA_USED, mInternalMediaSize); + + receiver.updateExactInternal(bundle); + } + + private void sendExternalApproximateUpdate() { + MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null; + if (receiver == null) { + return; + } + + Bundle bundle = new Bundle(); + bundle.putLong(TOTAL_SIZE, mExternalTotalSize); + bundle.putLong(AVAIL_SIZE, mExternalAvailSize); + + receiver.updateApproximateExternal(bundle); + } + + public interface MeasurementReceiver { + public void updateApproximateInternal(Bundle bundle); + + public void updateExactInternal(Bundle bundle); + + public void updateApproximateExternal(Bundle bundle); + } + + private class MeasurementHandler extends Handler { + public static final int MSG_MEASURE_INTERNAL = 1; + + public static final int MSG_MEASURE_EXTERNAL = 2; + + public static final int MSG_CONNECTED = 3; + + public static final int MSG_DISCONNECT = 4; + + public static final int MSG_COMPLETED = 5; + + public static final int MSG_INVALIDATE = 6; + + private List<String> mPendingApps = new ArrayList<String>(); + + private Object mLock = new Object(); + + private IMediaContainerService mDefaultContainer; + + private volatile boolean mBound = false; + + private volatile boolean mMeasured = false; + + private long mAppsSize = 0; + + private final WeakReference<Context> mContext; + + final private ServiceConnection mDefContainerConn = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + final IMediaContainerService imcs = IMediaContainerService.Stub + .asInterface(service); + mDefaultContainer = imcs; + mBound = true; + sendMessage(obtainMessage(MSG_CONNECTED, imcs)); + } + + public void onServiceDisconnected(ComponentName name) { + mBound = false; + removeMessages(MSG_CONNECTED); + } + }; + + public MeasurementHandler(Context context, Looper looper) { + super(looper); + mContext = new WeakReference<Context>(context); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_MEASURE_EXTERNAL: { + if (mMeasured) { + sendExternalApproximateUpdate(); + break; + } + + measureApproximateExternalStorage(); + break; + } + case MSG_MEASURE_INTERNAL: { + if (mMeasured) { + sendInternalExactUpdate(); + break; + } + + final Context context = (mContext != null) ? mContext.get() : null; + if (context == null) { + return; + } + + measureApproximateInternalStorage(); + + synchronized (mLock) { + if (mBound) { + removeMessages(MSG_DISCONNECT); + sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer)); + } else { + Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); + context.bindService(service, mDefContainerConn, + Context.BIND_AUTO_CREATE); + } + } + break; + } + case MSG_CONNECTED: { + IMediaContainerService imcs = (IMediaContainerService) msg.obj; + measureExactInternalStorage(imcs); + } + case MSG_DISCONNECT: { + synchronized (mLock) { + if (mBound) { + final Context context = (mContext != null) ? mContext.get() : null; + if (context == null) { + return; + } + + mBound = false; + context.unbindService(mDefContainerConn); + } + } + } + case MSG_COMPLETED: { + mMeasured = true; + sendInternalExactUpdate(); + break; + } + case MSG_INVALIDATE: { + mMeasured = false; + break; + } + } + } + + public void cleanUp() { + removeMessages(MSG_MEASURE_INTERNAL); + removeMessages(MSG_MEASURE_EXTERNAL); + + sendEmptyMessage(MSG_DISCONNECT); + } + + public void queuePackageMeasurementLocked(String packageName) { + mPendingApps.add(packageName); + } + + /** + * Request measurement of each package. + * + * @param pm PackageManager instance to query + */ + public void requestQueuedMeasurementsLocked(PackageManager pm) { + final int N = mPendingApps.size(); + for (int i = 0; i < N; i++) { + pm.getPackageSizeInfo(mPendingApps.get(i), mStatsObserver); + } + } + + final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() { + public void onGetStatsCompleted(PackageStats stats, boolean succeeded) { + if (succeeded) { + mAppsSize += stats.codeSize + stats.dataSize; + } + + synchronized (mPendingApps) { + mPendingApps.remove(stats.packageName); + + if (mPendingApps.size() == 0) { + mInternalAppsSize = mAppsSize; + + onInternalMeasurementComplete(); + } + } + } + }; + + private void onInternalMeasurementComplete() { + sendEmptyMessage(MSG_COMPLETED); + } + + private void measureApproximateInternalStorage() { + final File dataPath = Environment.getDataDirectory(); + final StatFs stat = new StatFs(dataPath.getPath()); + final long blockSize = stat.getBlockSize(); + final long totalBlocks = stat.getBlockCount(); + final long availableBlocks = stat.getAvailableBlocks(); + + final long totalSize = totalBlocks * blockSize; + final long availSize = availableBlocks * blockSize; + + mInternalTotalSize = totalSize; + mInternalAvailSize = availSize; + + sendInternalApproximateUpdate(); + } + + private void measureExactInternalStorage(IMediaContainerService imcs) { + Context context = mContext != null ? mContext.get() : null; + if (context == null) { + return; + } + + // We have to get installd to measure the package sizes. + PackageManager pm = context.getPackageManager(); + if (pm == null) { + return; + } + + long mediaSize; + try { + // TODO get these directories from somewhere + mediaSize = imcs.calculateDirectorySize("/data/media"); + } catch (Exception e) { + Log.i(TAG, "Could not read memory from default container service"); + return; + } + + mInternalMediaSize = mediaSize; + + final List<ApplicationInfo> apps = pm + .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS); + if (apps != null) { + synchronized (mPendingApps) { + for (int i = 0; i < apps.size(); i++) { + final ApplicationInfo info = apps.get(i); + queuePackageMeasurementLocked(info.packageName); + } + + requestQueuedMeasurementsLocked(pm); + } + } + + // Sending of the message back to the MeasurementReceiver is + // completed in the PackageObserver + } + + public void measureApproximateExternalStorage() { + File path = Environment.getExternalStorageDirectory(); + + StatFs stat = new StatFs(path.getPath()); + long blockSize = stat.getBlockSize(); + long totalBlocks = stat.getBlockCount(); + long availableBlocks = stat.getAvailableBlocks(); + + mExternalTotalSize = totalBlocks * blockSize; + mExternalAvailSize = availableBlocks * blockSize; + + sendExternalApproximateUpdate(); + } + } + + public void invalidate() { + mHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE); + } +} diff --git a/src/com/android/settings/deviceinfo/PercentageBarChart.java b/src/com/android/settings/deviceinfo/PercentageBarChart.java index e8fb62adf..2f174fb73 100644 --- a/src/com/android/settings/deviceinfo/PercentageBarChart.java +++ b/src/com/android/settings/deviceinfo/PercentageBarChart.java @@ -16,8 +16,12 @@ package com.android.settings.deviceinfo; +import com.android.settings.R; + import android.content.Context; +import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; @@ -28,7 +32,7 @@ import java.util.Collection; * */ public class PercentageBarChart extends View { - private final Paint mBackgroundPaint = new Paint(); + private final Paint mEmptyPaint = new Paint(); private Collection<Entry> mEntries; @@ -45,20 +49,39 @@ public class PercentageBarChart extends View { public PercentageBarChart(Context context, AttributeSet attrs) { super(context, attrs); - mBackgroundPaint.setARGB(255, 64, 64, 64); - mBackgroundPaint.setStyle(Paint.Style.FILL); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PercentageBarChart, 0, 0); + + int emptyColor = Color.BLACK; + + int n = a.getIndexCount(); + for (int i = 0; i < n; i++) { + int attr = a.getIndex(i); + + switch (attr) { + case R.styleable.PercentageBarChart_emptyColor: + emptyColor = a.getColor(attr, 0); + break; + } + } + + a.recycle(); + + mEmptyPaint.setColor(emptyColor); + mEmptyPaint.setStyle(Paint.Style.FILL); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - final int width = getWidth(); - final int height = getHeight(); + final int left = getPaddingLeft(); + final int right = getWidth() - getPaddingRight(); + final int top = getPaddingTop(); + final int bottom = getHeight() - getPaddingBottom(); - canvas.drawPaint(mBackgroundPaint); + final int width = right - left; - int lastX = 0; + int lastX = left; if (mEntries != null) { for (final Entry e : mEntries) { @@ -70,14 +93,16 @@ public class PercentageBarChart extends View { } final int nextX = lastX + entryWidth; - if (nextX >= width) { + if (nextX >= right) { break; } - canvas.drawRect(lastX, 0, nextX, height, e.paint); + canvas.drawRect(lastX, top, nextX, bottom, e.paint); lastX = nextX; } } + + canvas.drawRect(lastX, top, lastX + width, bottom, mEmptyPaint); } /** @@ -85,7 +110,7 @@ public class PercentageBarChart extends View { * calling {@link #invalidate()}. */ public void setBackgroundColor(int color) { - mBackgroundPaint.setColor(color); + mEmptyPaint.setColor(color); } /** |