From e4330890d6996bd1ad65bc6c5ab1dfb39f224ea4 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 13 Dec 2010 16:23:57 -0800 Subject: [PATCH 1/5] 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 --- res/layout/preference_memoryusage.xml | 4 +- res/values/attrs.xml | 5 + res/xml/device_info_memory.xml | 9 +- .../android/settings/deviceinfo/Memory.java | 428 +++++++----------- .../deviceinfo/MemoryMeasurement.java | 403 +++++++++++++++++ .../deviceinfo/PercentageBarChart.java | 45 +- 6 files changed, 610 insertions(+), 284 deletions(-) create mode 100644 src/com/android/settings/deviceinfo/MemoryMeasurement.java diff --git a/res/layout/preference_memoryusage.xml b/res/layout/preference_memoryusage.xml index 7972de9d41a..25fd8886511 100644 --- a/res/layout/preference_memoryusage.xml +++ b/res/layout/preference_memoryusage.xml @@ -22,5 +22,7 @@ android:gravity="center_vertical" android:id="@+id/percentage_bar_chart" android:paddingRight="?android:attr/scrollbarSize" - android:textAppearance="?android:attr/textAppearanceMedium"> + android:paddingTop="6dip" + android:paddingBottom="6dip" + emptyColor="@color/memory_avail"> \ No newline at end of file diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 9a123ddb22f..57d0f45ff77 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -42,4 +42,9 @@ + + + + + diff --git a/res/xml/device_info_memory.xml b/res/xml/device_info_memory.xml index 030dfa62d23..368862adf09 100644 --- a/res/xml/device_info_memory.xml +++ b/res/xml/device_info_memory.xml @@ -37,22 +37,19 @@ + android:key="memory_internal_chart"/> + android:summary="@string/memory_calculating_size"/> + android:summary="@string/memory_calculating_size"/> diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java index 4ef08d134f7..fb3728f3ac1 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 static final int MSG_UI_UPDATE_INTERNAL_EXACT = 2; + + // 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_UI_UPDATE_APPROXIMATE: - updateUiApproximate(); + 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_UI_UPDATE_EXACT: - updateUiExact(); - mMeasured = 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; + } + 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 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 List mPendingApps = new ArrayList(); - - 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); - } - - @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); - 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 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; - } - } - } - - 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 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 00000000000..ead9dd6ec06 --- /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 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(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 mPendingApps = new ArrayList(); + + 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 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); + } + + @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 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 e8fb62adf68..2f174fb7379 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 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); } /** From 043bcc04f31c503e4271a68da96fc72427948732 Mon Sep 17 00:00:00 2001 From: Fred Quintana Date: Thu, 16 Dec 2010 13:59:56 -0800 Subject: [PATCH 2/5] fix the TooManyDeletes to show up again It was moved so it was no longer in the location that SyncManager expected. I fixed it by (a) moving it to the SyncManager directory and by (b) changing the SyncManager to refer to it by class rather than by class name, which means it will now check for it at compile time, not run time. Bug: 3289922 Change-Id: I93fb78432e66ff14e1f750b06f79425c457399c2 --- AndroidManifest.xml | 9 -- res/values/strings.xml | 10 -- .../accounts/SyncActivityTooManyDeletes.java | 134 ------------------ 3 files changed, 153 deletions(-) delete mode 100644 src/com/android/settings/accounts/SyncActivityTooManyDeletes.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fdbfd157fcb..b82d931f799 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1073,15 +1073,6 @@ android:label="@string/header_add_an_account" android:theme="@android:style/Theme.Holo.DialogWhenLarge"/> - - - - - - - diff --git a/res/values/strings.xml b/res/values/strings.xml index 2b7ccb2537f..e1f0f96b85b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3052,8 +3052,6 @@ found in the list of installed applications. Account and Sync Settings - - Delete limit exceeded Back up settings @@ -3070,14 +3068,6 @@ found in the list of installed applications. Calendar Contacts - - There are %1$d deleted items for %2$s, account %3$s. What would you like to do? - - Delete the items. - - Undo the deletes. - - Do nothing for now. Welcome to Google sync! \nA Google approach to synchronizing data to allow access to your contacts, appointments, and more from wherever you are. diff --git a/src/com/android/settings/accounts/SyncActivityTooManyDeletes.java b/src/com/android/settings/accounts/SyncActivityTooManyDeletes.java deleted file mode 100644 index b31561cc160..00000000000 --- a/src/com/android/settings/accounts/SyncActivityTooManyDeletes.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.accounts; - -import com.android.settings.R; - -import android.accounts.Account; -import android.app.Activity; -import android.content.ContentResolver; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.LinearLayout; -import android.widget.ListAdapter; -import android.widget.ListView; -import android.widget.TextView; - -/** - * Presents multiple options for handling the case where a sync was aborted because there - * were too many pending deletes. One option is to force the delete, another is to rollback - * the deletes, the third is to do nothing. - */ -public class SyncActivityTooManyDeletes extends Activity - implements AdapterView.OnItemClickListener { - - private long mNumDeletes; - private Account mAccount; - private String mAuthority; - private String mProvider; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Bundle extras = getIntent().getExtras(); - if (extras == null) { - finish(); - return; - } - - mNumDeletes = extras.getLong("numDeletes"); - mAccount = (Account) extras.getParcelable("account"); - mAuthority = extras.getString("authority"); - mProvider = extras.getString("provider"); - - // the order of these must match up with the constants for position used in onItemClick - CharSequence[] options = new CharSequence[]{ - getResources().getText(R.string.sync_really_delete), - getResources().getText(R.string.sync_undo_deletes), - getResources().getText(R.string.sync_do_nothing) - }; - - ListAdapter adapter = new ArrayAdapter(this, - android.R.layout.simple_list_item_1, - android.R.id.text1, - options); - - ListView listView = new ListView(this); - listView.setAdapter(adapter); - listView.setItemsCanFocus(true); - listView.setOnItemClickListener(this); - - TextView textView = new TextView(this); - CharSequence tooManyDeletesDescFormat = - getResources().getText(R.string.sync_too_many_deletes_desc); - textView.setText(String.format(tooManyDeletesDescFormat.toString(), - mNumDeletes, mProvider, mAccount.name)); - - final LinearLayout ll = new LinearLayout(this); - ll.setOrientation(LinearLayout.VERTICAL); - final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0); - ll.addView(textView, lp); - ll.addView(listView, lp); - - // TODO: consider displaying the icon of the account type -// AuthenticatorDescription[] descs = AccountManager.get(this).getAuthenticatorTypes(); -// for (AuthenticatorDescription desc : descs) { -// if (desc.type.equals(mAccount.type)) { -// try { -// final Context authContext = createPackageContext(desc.packageName, 0); -// ImageView imageView = new ImageView(this); -// imageView.setImageDrawable(authContext.getResources().getDrawable(desc.iconId)); -// ll.addView(imageView, lp); -// } catch (PackageManager.NameNotFoundException e) { -// } -// break; -// } -// } - - setContentView(ll); - } - - public void onItemClick(AdapterView parent, View view, int position, long id) { - // the constants for position correspond to the items options array in onCreate() - if (position == 0) startSyncReallyDelete(); - else if (position == 1) startSyncUndoDeletes(); - finish(); - } - - private void startSyncReallyDelete() { - Bundle extras = new Bundle(); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS, true); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); - ContentResolver.requestSync(mAccount, mAuthority, extras); - } - - private void startSyncUndoDeletes() { - Bundle extras = new Bundle(); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS, true); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); - ContentResolver.requestSync(mAccount, mAuthority, extras); - } -} From 57acae3058bf3433c01a82b6585b345b4d6c9468 Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Fri, 17 Dec 2010 12:08:43 -0800 Subject: [PATCH 3/5] Add a scroll view to proxy settings so that it isn't obscured by the keyboard. Bug: 3267408 Change-Id: I06ab34538a741aa76301048ccbeb80a6e70219f3 --- res/layout/proxy.xml | 134 ++++++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 65 deletions(-) diff --git a/res/layout/proxy.xml b/res/layout/proxy.xml index 76f200ebf07..5fc42e701c4 100644 --- a/res/layout/proxy.xml +++ b/res/layout/proxy.xml @@ -1,6 +1,6 @@ - + - - - + android:scrollbars="vertical" > - + - + - + - + - + -