diff --git a/res/values/colors.xml b/res/values/colors.xml index 6879f9d7869..631fb194a7e 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -22,8 +22,8 @@ #333333 #77831A #476093 - #793A7F - #8E562A + #793A7F + #8E562A #7C3030 #ff9a9a9a diff --git a/res/values/strings.xml b/res/values/strings.xml index 80f3d571fcb..bb9360d67c4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -313,8 +313,8 @@ Clear Proxy port - - 8080 + + 8080 Bypass proxy for @@ -1100,8 +1100,8 @@ Link speed IP address - - 192.168.1.128 + + 192.168.1.128 EAP method @@ -1181,20 +1181,20 @@ Please type a network prefix length between 0 and 32. DNS 1 - - 8.8.8.8 + + 8.8.8.8 DNS 2 - - 4.4.4.4 + + 4.4.4.4 Gateway - - 192.168.1.1 + + 192.168.1.1 Network prefix length - - 24 + + 24 @@ -1591,7 +1591,7 @@ Media - Downloads + Downloads Pictures, Videos @@ -1618,10 +1618,10 @@ Mount SD card - - Mount the USB storage - - Mount the SD card + + + + Erase USB storage @@ -1630,9 +1630,7 @@ Erases all data on the internal USB storage, such as music and photos Erases all data on the SD card, such as music and photos - - Unavailable - + \u0020(Read-only) Unmount USB storage diff --git a/res/xml/device_info_memory.xml b/res/xml/device_info_memory.xml index aa36698840f..e905f39effe 100644 --- a/res/xml/device_info_memory.xml +++ b/res/xml/device_info_memory.xml @@ -17,54 +17,6 @@ - - - - - - + - - - - - - - - - - - - - - - - - diff --git a/src/com/android/settings/deviceinfo/Constants.java b/src/com/android/settings/deviceinfo/Constants.java deleted file mode 100644 index 9f494797d31..00000000000 --- a/src/com/android/settings/deviceinfo/Constants.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2010 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.deviceinfo; - -import android.os.Environment; - -import java.util.ArrayList; -import java.util.List; - -/** - * Some of the constants used in this package - */ -class Constants { - static final int MEDIA_INDEX = 0; - static final int DOWNLOADS_INDEX = 1; - static final int PIC_VIDEO_INDEX = 2; - static final int MUSIC_INDEX = 3; - static final int MEDIA_APPS_DATA_INDEX = 4; - static final int MEDIA_MISC_INDEX = 5; - static final int NUM_MEDIA_DIRS_TRACKED = MEDIA_MISC_INDEX + 1; - - static class MediaDirectory { - final String[] mDirPaths; - final String mKey; - final String mPreferenceName; - MediaDirectory(String pref, String debugInfo, String... paths) { - mDirPaths = paths; - mKey = debugInfo; - mPreferenceName = pref; - } - } - static final ArrayList mMediaDirs = new ArrayList(); - static final List ExclusionTargetsForMiscFiles = new ArrayList(); - static { - mMediaDirs.add(MEDIA_INDEX, - new MediaDirectory(null, - "/sdcard", - Environment.getExternalStorageDirectory().getAbsolutePath())); - mMediaDirs.add(DOWNLOADS_INDEX, - new MediaDirectory("memory_internal_downloads", - "/sdcard/download", - Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DOWNLOADS).getAbsolutePath())); - mMediaDirs.add(PIC_VIDEO_INDEX, - new MediaDirectory("memory_internal_dcim", - "/sdcard/pic_video", - Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DCIM).getAbsolutePath(), - Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_MOVIES).getAbsolutePath(), - Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_PICTURES).getAbsolutePath())); - mMediaDirs.add(MUSIC_INDEX, - new MediaDirectory("memory_internal_music", - "/sdcard/audio", - Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_MUSIC).getAbsolutePath(), - Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_ALARMS).getAbsolutePath(), - Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_NOTIFICATIONS).getAbsolutePath(), - Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_RINGTONES).getAbsolutePath(), - Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_PODCASTS).getAbsolutePath())); - mMediaDirs.add(MEDIA_APPS_DATA_INDEX, - new MediaDirectory(null, - "/sdcard/Android", - Environment.getExternalStorageAndroidDataDir().getAbsolutePath())); - mMediaDirs.add(MEDIA_MISC_INDEX, - new MediaDirectory("memory_internal_media_misc", - "misc on /sdcard", - "not relevant")); - // prepare a lit of strings representing dirpaths that should be skipped while looking - // for 'other' files - for (int j = 0; j < Constants.NUM_MEDIA_DIRS_TRACKED - 1; j++) { - String[] dirs = Constants.mMediaDirs.get(j).mDirPaths; - int len = dirs.length; - if (len > 0) { - for (int k = 0; k < len; k++) { - ExclusionTargetsForMiscFiles.add(dirs[k]); - } - } - // also add /sdcard/Android - ExclusionTargetsForMiscFiles.add( - Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android"); - } - } -} diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java index 1e10c58ac4d..955e57802b2 100644 --- a/src/com/android/settings/deviceinfo/Memory.java +++ b/src/com/android/settings/deviceinfo/Memory.java @@ -16,140 +16,52 @@ package com.android.settings.deviceinfo; -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.deviceinfo.MemoryMeasurement.MeasurementReceiver; - -import android.app.ActivityManager; import android.app.AlertDialog; import android.app.Dialog; -import android.app.DownloadManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.content.IntentFilter; -import android.content.DialogInterface.OnCancelListener; -import android.content.pm.ApplicationInfo; import android.content.res.Resources; -import android.graphics.drawable.ShapeDrawable; -import android.graphics.drawable.shapes.RectShape; import android.os.Bundle; import android.os.Environment; -import android.os.Handler; import android.os.IBinder; -import android.os.Message; +import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.storage.IMountService; import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; import android.preference.Preference; -import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; -import android.text.format.Formatter; import android.util.Log; import android.widget.Toast; -import java.util.List; +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; -public class Memory extends SettingsPreferenceFragment implements OnCancelListener, - MeasurementReceiver { +public class Memory extends SettingsPreferenceFragment implements OnCancelListener { private static final String TAG = "MemorySettings"; - private static final String MEMORY_SD_SIZE = "memory_sd_size"; - - private static final String MEMORY_SD_AVAIL = "memory_sd_avail"; - - private static final String MEMORY_SD_MOUNT_TOGGLE = "memory_sd_mount_toggle"; - - private static final String MEMORY_SD_FORMAT = "memory_sd_format"; - - private static final String MEMORY_SD_GROUP = "memory_sd"; - - private static final String MEMORY_INTERNAL_SIZE = "memory_internal_size"; - - private static final String MEMORY_INTERNAL_AVAIL = "memory_internal_avail"; - - private static final String MEMORY_INTERNAL_APPS = "memory_internal_apps"; - - private static final String MEMORY_INTERNAL_CHART = "memory_internal_chart"; - private static final int DLG_CONFIRM_UNMOUNT = 1; private static final int DLG_ERROR_UNMOUNT = 2; - private Resources mRes; + private Resources mResources; - // External storage preferences - private Preference mSdSize; - private Preference mSdAvail; - private Preference mSdMountToggle; - private Preference mSdFormat; - private PreferenceGroup mSdMountPreferenceGroup; - - // Internal storage preferences - private Preference mInternalSize; - private Preference mInternalAvail; - private Preference mInternalAppsUsage; - private final Preference[] mMediaPreferences = new Preference[Constants.NUM_MEDIA_DIRS_TRACKED]; - private UsageBarPreference mInternalUsageChart; - - // Internal storage chart colors - private int mInternalAppsColor; - private int mInternalAvailColor; - private int mInternalUsedColor; - - boolean mSdMountToggleAdded = true; + // The mountToggle Preference that has been clicked. + // The click event will be discarded if this value is not null. Reset to null after (un)mount. + private Preference mClickedMountToggle; + private String mClickedMountPoint; // Access using getMountService() private IMountService mMountService = null; private StorageManager mStorageManager = null; - // Updates the memory usage bar graph. - private static final int MSG_UI_UPDATE_INTERNAL_APPROXIMATE = 1; - - // Updates the memory usage bar graph. - 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_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_INTERNAL_EXACT: { - Bundle bundle = msg.getData(); - final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE); - final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE); - final long appsUsed = bundle.getLong(MemoryMeasurement.APPS_USED); - final long[] mediaSizes = new long[Constants.NUM_MEDIA_DIRS_TRACKED]; - for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) { - mediaSizes[i] = bundle.getLong(Constants.mMediaDirs.get(i).mKey); - } - updateUiExact(totalSize, availSize, appsUsed, mediaSizes); - 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 MemoryMeasurement mMeasurement; + private StorageVolumePreferenceCategory[] mStorageVolumePreferenceCategories; @Override public void onCreate(Bundle icicle) { @@ -162,84 +74,39 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen addPreferencesFromResource(R.xml.device_info_memory); - mRes = getResources(); - mSdSize = findPreference(MEMORY_SD_SIZE); - mSdAvail = findPreference(MEMORY_SD_AVAIL); - mSdMountToggle = findPreference(MEMORY_SD_MOUNT_TOGGLE); - mSdFormat = findPreference(MEMORY_SD_FORMAT); - mSdMountPreferenceGroup = (PreferenceGroup)findPreference(MEMORY_SD_GROUP); + mResources = getResources(); - if (Environment.isExternalStorageEmulated()) { - getPreferenceScreen().removePreference(mSdMountPreferenceGroup); - } - - mInternalSize = findPreference(MEMORY_INTERNAL_SIZE); - mInternalAppsColor = mRes.getColor(R.color.memory_apps_usage); - mInternalUsedColor = android.graphics.Color.GRAY; - mInternalAvailColor = mRes.getColor(R.color.memory_avail); - final int buttonWidth = (int) mRes.getDimension(R.dimen.device_memory_usage_button_width); - final int buttonHeight = (int) mRes.getDimension(R.dimen.device_memory_usage_button_height); - - // total available space - mInternalAvail = findPreference(MEMORY_INTERNAL_AVAIL); - mInternalAvail.setIcon(createRectShape(buttonHeight, buttonWidth, mInternalAvailColor)); - - // used by apps - mInternalAppsUsage = findPreference(MEMORY_INTERNAL_APPS); - mInternalAppsUsage.setIcon(createRectShape(buttonHeight, buttonWidth, mInternalAppsColor)); - - // space used by individual major directories on /sdcard - for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) { - // nothing to be displayed for certain entries in Constants.mMediaDirs - if (Constants.mMediaDirs.get(i).mPreferenceName == null) { - continue; + try { + IMountService mountService = IMountService.Stub.asInterface(ServiceManager + .getService("mount")); + Parcelable[] volumes = mountService.getVolumeList(); + int length = volumes.length; + mStorageVolumePreferenceCategories = new StorageVolumePreferenceCategory[length]; + for (int i = 0; i < length; i++) { + StorageVolume storageVolume = (StorageVolume) volumes[i]; + StorageVolumePreferenceCategory storagePreferenceCategory = + new StorageVolumePreferenceCategory(getActivity(), mResources, storageVolume, + i == 0); // The first volume is the primary volume + mStorageVolumePreferenceCategories[i] = storagePreferenceCategory; + getPreferenceScreen().addPreference(storagePreferenceCategory); + storagePreferenceCategory.init(); } - mMediaPreferences[i] = findPreference(Constants.mMediaDirs.get(i).mPreferenceName); - int color = 0; - switch (i) { - case Constants.DOWNLOADS_INDEX: - color = mRes.getColor(R.color.memory_downloads); - break; - case Constants.PIC_VIDEO_INDEX: - color = mRes.getColor(R.color.memory_video); - break; - case Constants.MUSIC_INDEX: - color = mRes.getColor(R.color.memory_audio); - break; - case Constants.MEDIA_MISC_INDEX: - color = mRes.getColor(R.color.memory_misc); - break; - } - mMediaPreferences[i].setIcon(createRectShape(buttonHeight, buttonWidth, color)); + } catch (Exception e) { + Log.e(TAG, "couldn't talk to MountService", e); } - mInternalUsageChart = (UsageBarPreference) findPreference(MEMORY_INTERNAL_CHART); - - mMeasurement = MemoryMeasurement.getInstance(getActivity()); - mMeasurement.setReceiver(this); - } - - private ShapeDrawable createRectShape(int height, int width, int color) { - ShapeDrawable shape = new ShapeDrawable(new RectShape()); - shape.setIntrinsicHeight(height); - shape.setIntrinsicWidth(width); - shape.getPaint().setColor(color); - return shape; } @Override public void onResume() { super.onResume(); - mMeasurement.setReceiver(this); IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED); intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); intentFilter.addDataScheme("file"); - getActivity().registerReceiver(mReceiver, intentFilter); + getActivity().registerReceiver(mMediaScannerReceiver, intentFilter); - mMeasurement.invalidate(); - if (!Environment.isExternalStorageEmulated()) { - mMeasurement.measureExternal(); + for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) { + mStorageVolumePreferenceCategories[i].onResume(); } - mMeasurement.measureInternal(); } StorageEventListener mStorageListener = new StorageEventListener() { @@ -248,8 +115,12 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen Log.i(TAG, "Received storage state changed notification that " + path + " changed state from " + oldState + " to " + newState); - if (!Environment.isExternalStorageEmulated()) { - mMeasurement.measureExternal(); + for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) { + StorageVolumePreferenceCategory svpc = mStorageVolumePreferenceCategories[i]; + if (path.equals(svpc.getMountPoint())) { + svpc.onStorageStateChanged(); + break; + } } } }; @@ -257,8 +128,10 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen @Override public void onPause() { super.onPause(); - getActivity().unregisterReceiver(mReceiver); - mMeasurement.cleanUp(); + getActivity().unregisterReceiver(mMediaScannerReceiver); + for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) { + mStorageVolumePreferenceCategories[i].onPause(); + } } @Override @@ -283,60 +156,38 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { - if (preference == mSdMountToggle) { - String status = Environment.getExternalStorageState(); - if (status.equals(Environment.MEDIA_MOUNTED)) { - unmount(); - } else { - mount(); + for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) { + StorageVolumePreferenceCategory svpc = mStorageVolumePreferenceCategories[i]; + Intent intent = svpc.intentForClick(preference); + if (intent != null) { + startActivity(intent); + return true; } - return true; - } else if (preference == mSdFormat) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setClass(getActivity(), com.android.settings.MediaFormat.class); - startActivity(intent); - return true; - } else if (preference == mInternalAppsUsage) { - Intent intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE); - intent.setClass(getActivity(), - com.android.settings.Settings.ManageApplicationsActivity.class); - startActivity(intent); - return true; - } else if (preference == mMediaPreferences[Constants.DOWNLOADS_INDEX]) { - Intent intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS) - .putExtra(DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true); - startActivity(intent); - return true; - } else if (preference == mMediaPreferences[Constants.MUSIC_INDEX]) { - Intent intent = new Intent("android.intent.action.GET_CONTENT"); - intent.setType("audio/mp3"); - startActivity(intent); - return true; - } else if (preference == mMediaPreferences[Constants.PIC_VIDEO_INDEX]) { - Intent intent = new Intent("android.intent.action.GET_CONTENT"); - intent.setType("image/jpeg"); - startActivity(intent); - return true; - } else if (preference == mMediaPreferences[Constants.MEDIA_MISC_INDEX]) { - Context context = getActivity().getApplicationContext(); - if (MemoryMeasurement.getInstance(context).isSizeOfMiscCategoryNonZero()) { - startActivity(new Intent(context, MiscFilesHandler.class)); + + boolean mountToggleClicked = svpc.mountToggleClicked(preference); + if (mountToggleClicked && mClickedMountToggle == null) { + mClickedMountToggle = preference; + mClickedMountPoint = svpc.getMountPoint(); + String state = svpc.getStorageVolumeState(); + if (state.equals(Environment.MEDIA_MOUNTED) || + state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { + unmount(); + } else { + mount(); + } + return true; } - return true; } return false; } - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mMediaScannerReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - mMeasurement.invalidate(); - - if (!Environment.isExternalStorageEmulated()) { - mMeasurement.measureExternal(); + for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) { + mStorageVolumePreferenceCategories[i].onMediaScannerFinished(); } - mMeasurement.measureInternal(); } }; @@ -348,7 +199,7 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen .setTitle(R.string.dlg_confirm_unmount_title) .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - doUnmount(true); + doUnmount(); }}) .setNegativeButton(R.string.cancel, null) .setMessage(R.string.dlg_confirm_unmount_text) @@ -368,28 +219,27 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen super.showDialog(id); switch (id) { - case DLG_CONFIRM_UNMOUNT: - case DLG_ERROR_UNMOUNT: - setOnCancelListener(this); - break; + case DLG_CONFIRM_UNMOUNT: + case DLG_ERROR_UNMOUNT: + setOnCancelListener(this); + break; } } - private void doUnmount(boolean force) { + private void doUnmount() { // Present a toast here Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show(); IMountService mountService = getMountService(); - String extStoragePath = Environment.getExternalStorageDirectory().toString(); try { - mSdMountToggle.setEnabled(false); - mSdMountToggle.setTitle(mRes.getString(R.string.sd_ejecting_title)); - mSdMountToggle.setSummary(mRes.getString(R.string.sd_ejecting_summary)); - mountService.unmountVolume(extStoragePath, force); + mClickedMountToggle.setEnabled(false); + mClickedMountToggle.setTitle(mResources.getString(R.string.sd_ejecting_title)); + mClickedMountToggle.setSummary(mResources.getString(R.string.sd_ejecting_summary)); + mountService.unmountVolume(mClickedMountPoint, true); } catch (RemoteException e) { - // Informative dialog to user that - // unmount failed. + // Informative dialog to user that unmount failed. showDialogInner(DLG_ERROR_UNMOUNT); } + mClickedMountToggle = null; } private void showDialogInner(int id) { @@ -398,17 +248,21 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen } private boolean hasAppsAccessingStorage() throws RemoteException { - String extStoragePath = Environment.getExternalStorageDirectory().toString(); IMountService mountService = getMountService(); - int stUsers[] = mountService.getStorageUsers(extStoragePath); + int stUsers[] = mountService.getStorageUsers(mClickedMountPoint); if (stUsers != null && stUsers.length > 0) { return true; } + // TODO FIXME Parameterize with mountPoint and uncomment. + // On HC-MR2, no apps can be installed on sd and the emulated internal storage is not + // removable: application cannot interfere with unmount + /* ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); List list = am.getRunningExternalApplications(); if (list != null && list.size() > 0) { return true; } + */ return false; } @@ -419,12 +273,13 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen // Present dialog to user showDialogInner(DLG_CONFIRM_UNMOUNT); } else { - doUnmount(true); + doUnmount(); } } catch (RemoteException e) { // Very unlikely. But present an error dialog anyway Log.e(TAG, "Is MountService running?"); showDialogInner(DLG_ERROR_UNMOUNT); + mClickedMountToggle = null; } } @@ -432,151 +287,17 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen IMountService mountService = getMountService(); try { if (mountService != null) { - mountService.mountVolume(Environment.getExternalStorageDirectory().toString()); + mountService.mountVolume(mClickedMountPoint); } else { Log.e(TAG, "Mount service is null, can't mount"); } } catch (RemoteException ex) { + // Not much can be done } - } - - private void updateUiExact(long totalSize, long availSize, long appsSize, long[] mediaSizes) { - // There are other things that can take up storage, but we didn't measure it. - // add that unaccounted-for-usage to Apps Usage - long appsPlusRemaining = totalSize - availSize - mediaSizes[Constants.DOWNLOADS_INDEX] - - mediaSizes[Constants.PIC_VIDEO_INDEX] - mediaSizes[Constants.MUSIC_INDEX] - - mediaSizes[Constants.MEDIA_MISC_INDEX]; - mInternalSize.setSummary(formatSize(totalSize)); - mInternalAvail.setSummary(formatSize(availSize)); - mInternalAppsUsage.setSummary(formatSize(appsPlusRemaining)); - - mInternalUsageChart.clear(); - mInternalUsageChart.addEntry(appsPlusRemaining / (float) totalSize, mInternalAppsColor); - - for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) { - if (Constants.mMediaDirs.get(i).mPreferenceName == null) { - continue; - } - this.mMediaPreferences[i].setSummary(formatSize(mediaSizes[i])); - // don't add entry to color chart for media usage and for zero-sized dirs - if (i != Constants.MEDIA_INDEX && mediaSizes[i] > 0) { - int color = 0; - switch (i) { - case Constants.DOWNLOADS_INDEX: - color = mRes.getColor(R.color.memory_downloads); - break; - case Constants.PIC_VIDEO_INDEX: - color = mRes.getColor(R.color.memory_video); - break; - case Constants.MUSIC_INDEX: - color = mRes.getColor(R.color.memory_audio); - break; - case Constants.MEDIA_MISC_INDEX: - color = mRes.getColor(R.color.memory_misc); - break; - } - mInternalUsageChart.addEntry(mediaSizes[i] / (float) totalSize, color); - } - } - mInternalUsageChart.addEntry(availSize / (float) totalSize, mInternalAvailColor); - mInternalUsageChart.commit(); - } - - private void updateUiApproximate(long totalSize, long availSize) { - mInternalSize.setSummary(formatSize(totalSize)); - mInternalAvail.setSummary(formatSize(availSize)); - - final long usedSize = totalSize - availSize; - - mInternalUsageChart.clear(); - 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); + mClickedMountToggle = null; } public void onCancel(DialogInterface dialog) { - // 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); + mClickedMountToggle = null; } } diff --git a/src/com/android/settings/deviceinfo/MemoryMeasurement.java b/src/com/android/settings/deviceinfo/MemoryMeasurement.java deleted file mode 100644 index 1b42bc10df2..00000000000 --- a/src/com/android/settings/deviceinfo/MemoryMeasurement.java +++ /dev/null @@ -1,505 +0,0 @@ -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.Collections; -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) - * Application measurements (using PackageManager) - * - * Then the calling application would just specify the type and an argument. - * This class would keep track of it while the calling application would - * decide on how to use it. - */ -public class MemoryMeasurement { - private static final String TAG = "MemorySettings"; - 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"; - - private long[] mMediaSizes = new long[Constants.NUM_MEDIA_DIRS_TRACKED]; - - 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 mInternalAppsSize; - - // External memory fields - private long mExternalTotalSize; - - private long mExternalAvailSize; - List mFileInfoForMisc; - - 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) { - if (mReceiver == null || mReceiver.get() == null) { - 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) { - if (LOGV) { - Log.i(TAG, "measurements dropped because receiver is null! wasted effort"); - } - return; - } - - Bundle bundle = new Bundle(); - bundle.putLong(TOTAL_SIZE, mInternalTotalSize); - bundle.putLong(AVAIL_SIZE, mInternalAvailSize); - bundle.putLong(APPS_USED, mInternalAppsSize); - for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) { - bundle.putLong(Constants.mMediaDirs.get(i).mKey, mMediaSizes[i]); - } - - 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 Object mLock = new Object(); - - private IMediaContainerService mDefaultContainer; - - private volatile boolean mBound = false; - - private volatile boolean mMeasured = false; - - private StatsObserver mStatsObserver; - - 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); - break; - } - case MSG_DISCONNECT: { - synchronized (mLock) { - if (mBound) { - final Context context = (mContext != null) ? mContext.get() : null; - if (context == null) { - return; - } - - mBound = false; - context.unbindService(mDefContainerConn); - } - } - break; - } - 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); - } - - /** - * Request measurement of each package. - * - * @param pm PackageManager instance to query - */ - public void requestQueuedMeasurementsLocked(PackageManager pm) { - final List appsList = mStatsObserver.getAppsList(); - final int N = appsList.size(); - for (int i = 0; i < N; i++) { - pm.getPackageSizeInfo(appsList.get(i), mStatsObserver); - } - } - - private class StatsObserver extends IPackageStatsObserver.Stub { - private long mAppsSizeForThisStatsObserver = 0; - private final List mAppsList = new ArrayList(); - public void onGetStatsCompleted(PackageStats stats, boolean succeeded) { - if (!mStatsObserver.equals(this)) { - // this callback's class object is no longer in use. ignore this callback. - return; - } - if (succeeded) { - mAppsSizeForThisStatsObserver += stats.codeSize + stats.dataSize + - stats.externalCacheSize + stats.externalDataSize + - stats.externalMediaSize + stats.externalObbSize; - } - - synchronized (mAppsList) { - mAppsList.remove(stats.packageName); - - if (mAppsList.size() == 0) { - mInternalAppsSize = mAppsSizeForThisStatsObserver; - - onInternalMeasurementComplete(); - } - } - } - - public void queuePackageMeasurementLocked(String packageName) { - mAppsList.add(packageName); - } - public List getAppsList() { - return mAppsList; - } - } - - 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; - } - // measure sizes for all except "media_misc" - which is computed - for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED - 1; i++) { - mMediaSizes[i] = 0; - String[] dirs = Constants.mMediaDirs.get(i).mDirPaths; - int len = dirs.length; - if (len > 0) { - for (int k = 0; k < len; k++) { - long dirSize = getSize(imcs, dirs[k]); - mMediaSizes[i] += dirSize; - if (LOGV) { - Log.i(TAG, "size of " + dirs[k] + ": " + dirSize); - } - } - } - } - - // compute the size of "misc" - mMediaSizes[Constants.MEDIA_MISC_INDEX] = mMediaSizes[Constants.MEDIA_INDEX]; - for (int i = 1; i < Constants.NUM_MEDIA_DIRS_TRACKED - 1; i++) { - mMediaSizes[Constants.MEDIA_MISC_INDEX] -= mMediaSizes[i]; - } - if (LOGV) { - Log.i(TAG, "media_misc size: " + mMediaSizes[Constants.MEDIA_MISC_INDEX]); - } - - // compute the sizes of each of the files/directories under 'misc' category - measureSizesOfMisc(imcs); - - // compute apps sizes - final List apps = pm - .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES - | PackageManager.GET_DISABLED_COMPONENTS); - if (apps != null) { - // 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); - } - - requestQueuedMeasurementsLocked(pm); - } - } - - // Sending of the message back to the MeasurementReceiver is - // completed in the PackageObserver - } - private void measureSizesOfMisc(IMediaContainerService imcs) { - File top = Environment.getExternalStorageDirectory(); - mFileInfoForMisc = new ArrayList(); - File[] files = top.listFiles(); - int len = files.length; - if (len == 0) { - return; - } - // get sizes of all top level nodes in /sdcard dir except the ones already computed... - long counter = 0; - for (int i = 0; i < len; i++) { - String path = files[i].getAbsolutePath(); - if (Constants.ExclusionTargetsForMiscFiles.contains(path)) { - continue; - } - if (files[i].isFile()) { - mFileInfoForMisc.add(new FileInfo(path, files[i].length(), counter++)); - } else if (files[i].isDirectory()) { - long dirSize = getSize(imcs, path); - mFileInfoForMisc.add(new FileInfo(path, dirSize, counter++)); - } else { - } - } - // sort the list of FileInfo objects collected above in descending order of their sizes - Collections.sort(mFileInfoForMisc); - } - - private long getSize(IMediaContainerService imcs, String dir) { - try { - long size = imcs.calculateDirectorySize(dir); - return size; - } catch (Exception e) { - Log.w(TAG, "Could not read memory from default container service for " + - dir, e); - return -1; - } - } - - 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); - } - - boolean isSizeOfMiscCategoryNonZero() { - return mFileInfoForMisc != null && mFileInfoForMisc.size() > 0; - } - - static class FileInfo implements Comparable { - String mFileName; - long mSize; - long mId; - FileInfo(String fileName, long size, long id) { - mFileName = fileName; - mSize = size; - mId = id; - } - @Override - public int compareTo(FileInfo that) { - if (this == that || mSize == that.mSize) return 0; - else if (mSize < that.mSize) return 1; // for descending sort - else return -1; - } - @Override - public String toString() { - return mFileName + " : " + mSize + ", id:" + mId; - } - } -} diff --git a/src/com/android/settings/deviceinfo/MiscFilesHandler.java b/src/com/android/settings/deviceinfo/MiscFilesHandler.java index 15951c959bc..eae68611a56 100644 --- a/src/com/android/settings/deviceinfo/MiscFilesHandler.java +++ b/src/com/android/settings/deviceinfo/MiscFilesHandler.java @@ -16,12 +16,11 @@ package com.android.settings.deviceinfo; -import com.android.settings.R; -import com.android.settings.deviceinfo.MemoryMeasurement.FileInfo; - +import android.app.Activity; import android.app.ListActivity; import android.content.Context; import android.os.Bundle; +import android.os.storage.StorageVolume; import android.text.format.Formatter; import android.util.Log; import android.util.SparseBooleanArray; @@ -39,6 +38,9 @@ import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ListView; +import com.android.settings.R; +import com.android.settings.deviceinfo.StorageMeasurement.FileInfo; + import java.io.File; import java.util.ArrayList; import java.util.List; @@ -107,7 +109,7 @@ public class MiscFilesHandler extends ListActivity { //item not selected continue; } - if (MemoryMeasurement.LOGV) { + if (StorageMeasurement.LOGV) { Log.i(TAG, "deleting: " + mAdapter.getItem(i)); } // delete the file @@ -156,6 +158,7 @@ public class MiscFilesHandler extends ListActivity { } public void onDestroyActionMode(ActionMode mode) { + // This block intentionally left blank } public void onItemCheckedStateChanged(ActionMode mode, int position, long id, @@ -181,17 +184,21 @@ public class MiscFilesHandler extends ListActivity { } } - public class MemoryMearurementAdapter extends BaseAdapter { - private ArrayList mData = null; + class MemoryMearurementAdapter extends BaseAdapter { + private ArrayList mData = null; private long mDataSize = 0; private Context mContext; - public MemoryMearurementAdapter(Context context) { - mContext = context; - MemoryMeasurement mMeasurement = MemoryMeasurement.getInstance(context); - mData = (ArrayList)mMeasurement.mFileInfoForMisc; + public MemoryMearurementAdapter(Activity activity) { + mContext = activity; + final Bundle extras = activity.getIntent().getExtras(); + final StorageVolume storageVolume = extras.getParcelable( + StorageVolumePreferenceCategory.STORAGE_VOLUME); + StorageMeasurement mMeasurement = + StorageMeasurement.getInstance(activity, storageVolume, false); + mData = (ArrayList) mMeasurement.mFileInfoForMisc; if (mData != null) { - for (MemoryMeasurement.FileInfo info : mData) { + for (StorageMeasurement.FileInfo info : mData) { mDataSize += info.mSize; } } @@ -203,7 +210,7 @@ public class MiscFilesHandler extends ListActivity { } @Override - public MemoryMeasurement.FileInfo getItem(int position) { + public StorageMeasurement.FileInfo getItem(int position) { if (mData == null || mData.size() <= position) { return null; } @@ -217,13 +224,14 @@ public class MiscFilesHandler extends ListActivity { } return mData.get(position).mId; } + public void removeAll(List objs) { if (mData == null) { return; } for (Object o : objs) { mData.remove(o); - mDataSize -= ((MemoryMeasurement.FileInfo) o).mSize; + mDataSize -= ((StorageMeasurement.FileInfo) o).mSize; } } diff --git a/src/com/android/settings/deviceinfo/PercentageBarChart.java b/src/com/android/settings/deviceinfo/PercentageBarChart.java index 7d023010efd..0c71c129cca 100644 --- a/src/com/android/settings/deviceinfo/PercentageBarChart.java +++ b/src/com/android/settings/deviceinfo/PercentageBarChart.java @@ -99,6 +99,7 @@ public class PercentageBarChart extends View { * Sets the background for this chart. Callers are responsible for later * calling {@link #invalidate()}. */ + @Override public void setBackgroundColor(int color) { mEmptyPaint.setColor(color); } diff --git a/src/com/android/settings/deviceinfo/StorageMeasurement.java b/src/com/android/settings/deviceinfo/StorageMeasurement.java new file mode 100644 index 00000000000..14b5108a245 --- /dev/null +++ b/src/com/android/settings/deviceinfo/StorageMeasurement.java @@ -0,0 +1,527 @@ +/* + * Copyright (C) 2011 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.deviceinfo; + +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.os.storage.StorageVolume; +import android.util.Log; + +import com.android.internal.app.IMediaContainerService; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 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) + * Application measurements (using PackageManager) + * + * Then the calling application would just specify the type and an argument. + * This class would keep track of it while the calling application would + * decide on how to use it. + */ +public class StorageMeasurement { + private static final String TAG = "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"; + + private static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( + DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService"); + + private final MeasurementHandler mHandler; + + private static Map sInstances = + new ConcurrentHashMap(); + + private volatile WeakReference mReceiver; + + private long mTotalSize; + private long mAvailSize; + private long mAppsSize; + private long mDownloadsSize; + private long mMiscSize; + private long[] mMediaSizes = new long[StorageVolumePreferenceCategory.sMediaCategories.length]; + + final private StorageVolume mStorageVolume; + final private boolean mIsPrimary; + + List mFileInfoForMisc; + + public interface MeasurementReceiver { + public void updateApproximate(Bundle bundle); + public void updateExact(Bundle bundle); + } + + private StorageMeasurement(Context context, StorageVolume storageVolume, boolean isPrimary) { + mStorageVolume = storageVolume; + mIsPrimary = isPrimary; + + // Start the thread that will measure the disk usage. + final HandlerThread handlerThread = new HandlerThread("MemoryMeasurement"); + handlerThread.start(); + mHandler = new MeasurementHandler(context, handlerThread.getLooper()); + } + + /** + * 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, + boolean isPrimary) { + if (sInstances.containsKey(storageVolume)) { + return sInstances.get(storageVolume); + } else { + StorageMeasurement storageMeasurement = + new StorageMeasurement(context.getApplicationContext(), storageVolume, isPrimary); + sInstances.put(storageVolume, storageMeasurement); + return storageMeasurement; + } + } + + public void setReceiver(MeasurementReceiver receiver) { + if (mReceiver == null || mReceiver.get() == null) { + mReceiver = new WeakReference(receiver); + } + } + + public void measure() { + if (!mHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) { + mHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE); + } + } + + public void cleanUp() { + mReceiver = null; + mHandler.removeMessages(MeasurementHandler.MSG_MEASURE); + mHandler.sendEmptyMessage(MeasurementHandler.MSG_DISCONNECT); + } + + public void invalidate() { + mHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE); + } + + private void sendInternalApproximateUpdate() { + MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null; + if (receiver == null) { + return; + } + + Bundle bundle = new Bundle(); + bundle.putLong(TOTAL_SIZE, mTotalSize); + bundle.putLong(AVAIL_SIZE, mAvailSize); + + receiver.updateApproximate(bundle); + } + + private void sendExactUpdate() { + MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null; + if (receiver == null) { + if (LOGV) { + Log.i(TAG, "measurements dropped because receiver is null! wasted effort"); + } + return; + } + + 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); + + receiver.updateExact(bundle); + } + + 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(); + + private IMediaContainerService mDefaultContainer; + + private volatile boolean mBound = false; + + private volatile boolean mMeasured = false; + + private StatsObserver mStatsObserver; + + 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: { + if (mMeasured) { + sendExactUpdate(); + break; + } + + final Context context = (mContext != null) ? mContext.get() : null; + if (context == null) { + return; + } + + measureApproximateStorage(); + + 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; + measureExactStorage(imcs); + break; + } + case MSG_DISCONNECT: { + synchronized (mLock) { + if (mBound) { + final Context context = (mContext != null) ? mContext.get() : null; + if (context == null) { + return; + } + + mBound = false; + context.unbindService(mDefContainerConn); + } + } + break; + } + case MSG_COMPLETED: { + mMeasured = true; + sendExactUpdate(); + break; + } + case MSG_INVALIDATE: { + mMeasured = false; + break; + } + } + } + + /** + * Request measurement of each package. + * + * @param pm PackageManager instance to query + */ + public void requestQueuedMeasurementsLocked(PackageManager pm) { + final String[] appsList = mStatsObserver.getAppsList(); + final int N = appsList.length; + for (int i = 0; i < N; i++) { + pm.getPackageSizeInfo(appsList[i], mStatsObserver); + } + } + + private class StatsObserver extends IPackageStatsObserver.Stub { + private long mAppsSizeForThisStatsObserver = 0; + private final List mAppsList = new ArrayList(); + + public void onGetStatsCompleted(PackageStats stats, boolean succeeded) { + if (!mStatsObserver.equals(this)) { + // this callback's class object is no longer in use. ignore this callback. + return; + } + + if (succeeded) { + mAppsSizeForThisStatsObserver += stats.codeSize + stats.dataSize + + stats.externalCacheSize + stats.externalDataSize + + 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() { + final StatFs stat = new StatFs(mStorageVolume.getPath()); + final long blockSize = stat.getBlockSize(); + final long totalBlocks = stat.getBlockCount(); + final long availableBlocks = stat.getAvailableBlocks(); + + mTotalSize = totalBlocks * blockSize; + mAvailSize = availableBlocks * blockSize; + + sendInternalApproximateUpdate(); + } + + private void measureExactStorage(IMediaContainerService imcs) { + 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; + } + } + + /* 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 = Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); + mDownloadsSize = getDirectorySize(imcs, downloadsPath); + } else { + mDownloadsSize = 0; + } + + // Misc + mMiscSize = 0; + if (mIsPrimary) { + measureSizesOfMisc(imcs); + } + + // Apps + // We have to get installd to measure the package sizes. + PackageManager pm = context.getPackageManager(); + if (pm == null) { + return; + } + final List apps; + if (mIsPrimary) { + apps = pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES | + PackageManager.GET_DISABLED_COMPONENTS); + } else { + // TODO also measure apps installed on the SD card + apps = Collections.emptyList(); + } + + if (apps != null && apps.size() > 0) { + // 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); + } + } + + requestQueuedMeasurementsLocked(pm); + // Sending of the message back to the MeasurementReceiver is + // completed in the PackageObserver + } else { + onInternalMeasurementComplete(); + } + } + } + + private long getDirectorySize(IMediaContainerService imcs, String dir) { + try { + return imcs.calculateDirectorySize(dir); + } catch (Exception e) { + Log.w(TAG, "Could not read memory from default container service for " + dir, e); + return 0; + } + } + + long getMiscSize() { + return mMiscSize; + } + + private void measureSizesOfMisc(IMediaContainerService imcs) { + File top = new File(mStorageVolume.getPath()); + mFileInfoForMisc = new ArrayList(); + File[] files = top.listFiles(); + final int len = files.length; + // 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)) { + continue; + } + if (files[i].isFile()) { + final long fileSize = files[i].length(); + mFileInfoForMisc.add(new FileInfo(path, fileSize, counter++)); + mMiscSize += fileSize; + } else if (files[i].isDirectory()) { + final long dirSize = getDirectorySize(imcs, path); + mFileInfoForMisc.add(new FileInfo(path, dirSize, counter++)); + mMiscSize += 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); + } + + static class FileInfo implements Comparable { + final String mFileName; + final long mSize; + final long mId; + + FileInfo(String fileName, long size, long id) { + mFileName = fileName; + mSize = size; + mId = id; + } + + @Override + public int compareTo(FileInfo that) { + if (this == that || mSize == that.mSize) return 0; + else return (mSize < that.mSize) ? 1 : -1; // for descending sort + } + + @Override + public String toString() { + return mFileName + " : " + mSize + ", id:" + mId; + } + } +} diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java new file mode 100644 index 00000000000..7b52bcbd1a6 --- /dev/null +++ b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2011 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.deviceinfo; + +import android.app.DownloadManager; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RectShape; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.os.ServiceManager; +import android.os.storage.IMountService; +import android.os.storage.StorageVolume; +import android.preference.Preference; +import android.preference.PreferenceCategory; +import android.text.format.Formatter; + +import com.android.settings.R; +import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver; + +import java.util.HashSet; +import java.util.Set; + +public class StorageVolumePreferenceCategory extends PreferenceCategory implements + MeasurementReceiver { + + static final int TOTAL_SIZE = 0; + static final int APPLICATIONS = 1; + static final int DCIM = 2; // Pictures and Videos + static final int MUSIC = 3; + static final int DOWNLOADS = 4; + static final int MISC = 5; + static final int AVAILABLE = 6; + + private UsageBarPreference mUsageBarPreference; + private Preference[] mPreferences; + private Preference mMountTogglePreference; + private Preference mFormatPreference; + private int[] mColors; + + private Resources mResources; + + private StorageVolume mStorageVolume; + + private StorageMeasurement mMeasurement; + + static class CategoryInfo { + final int mTitle; + final int mColor; + + public CategoryInfo(int title, int color) { + mTitle = title; + mColor = color; + } + } + + static final CategoryInfo[] sCategoryInfos = new CategoryInfo[] { + new CategoryInfo(R.string.memory_size, 0), + new CategoryInfo(R.string.memory_apps_usage, R.color.memory_apps_usage), + new CategoryInfo(R.string.memory_dcim_usage, R.color.memory_dcim), + new CategoryInfo(R.string.memory_music_usage, R.color.memory_music), + new CategoryInfo(R.string.memory_downloads_usage, R.color.memory_downloads), + new CategoryInfo(R.string.memory_media_misc_usage, R.color.memory_misc), + new CategoryInfo(R.string.memory_available, R.color.memory_avail), + }; + + public static final Set sPathsExcludedForMisc = new HashSet(); + + static class MediaCategory { + final String[] mDirPaths; + final int mCategory; + //final int mMediaType; + + public MediaCategory(int 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 = Environment.getExternalStoragePublicDirectory(name). + getAbsolutePath(); + mDirPaths[i] = path; + sPathsExcludedForMisc.add(path); + } + } + } + + static final MediaCategory[] sMediaCategories = new MediaCategory[] { + new MediaCategory(DCIM, Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES, + Environment.DIRECTORY_PICTURES), + new MediaCategory(MUSIC, Environment.DIRECTORY_MUSIC, Environment.DIRECTORY_ALARMS, + Environment.DIRECTORY_NOTIFICATIONS, Environment.DIRECTORY_RINGTONES, + Environment.DIRECTORY_PODCASTS) + }; + + static { + // Downloads + sPathsExcludedForMisc.add(Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()); + // Apps + sPathsExcludedForMisc.add(Environment.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; + + // Key for the extra StorageVolume bundle added to the Misc intent. + static final String STORAGE_VOLUME = "storage_volume"; + + private Handler mUpdateHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UI_UPDATE_APPROXIMATE: { + Bundle bundle = msg.getData(); + final long totalSize = bundle.getLong(StorageMeasurement.TOTAL_SIZE); + final long availSize = bundle.getLong(StorageMeasurement.AVAIL_SIZE); + updateApproximate(totalSize, availSize); + break; + } + case MSG_UI_UPDATE_EXACT: { + 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); + updateExact(totalSize, availSize, appsUsed, downloadsSize, miscSize, + mediaSizes); + break; + } + } + } + }; + + public StorageVolumePreferenceCategory(Context context, Resources resources, + StorageVolume storageVolume, boolean isPrimary) { + super(context); + mResources = resources; + mStorageVolume = storageVolume; + setTitle(storageVolume.getDescription()); + mMeasurement = StorageMeasurement.getInstance(context, storageVolume, isPrimary); + mMeasurement.setReceiver(this); + } + + public void init() { + mUsageBarPreference = new UsageBarPreference(getContext()); + + final int width = (int) mResources.getDimension(R.dimen.device_memory_usage_button_width); + final int height = (int) mResources.getDimension(R.dimen.device_memory_usage_button_height); + + final int numberOfCategories = sCategoryInfos.length; + mPreferences = new Preference[numberOfCategories]; + mColors = new int[numberOfCategories]; + for (int i = 0; i < numberOfCategories; i++) { + final Preference preference = new Preference(getContext()); + mPreferences[i] = preference; + preference.setTitle(sCategoryInfos[i].mTitle); + preference.setSummary(R.string.memory_calculating_size); + if (i != TOTAL_SIZE) { + // TOTAL_SIZE has no associated color + mColors[i] = mResources.getColor(sCategoryInfos[i].mColor); + preference.setIcon(createRectShape(width, height, mColors[i])); + } + } + + mMountTogglePreference = new Preference(getContext()); + mMountTogglePreference.setTitle(R.string.sd_eject); + mMountTogglePreference.setSummary(R.string.sd_eject_summary); + + mFormatPreference = new Preference(getContext()); + mFormatPreference.setTitle(R.string.sd_format); + mFormatPreference.setSummary(R.string.sd_format_summary); + } + + public String getMountPoint() { + return mStorageVolume.getPath(); + } + + public String getStorageVolumeState() { + try { + IMountService mountService = + IMountService.Stub.asInterface(ServiceManager.getService("mount")); + return mountService.getVolumeState(getMountPoint()); + } catch (Exception rex) { + return Environment.MEDIA_REMOVED; + } + } + + /** + * Successive mounts can change the list of visible preferences. + * This makes sure all preferences are visible and displayed in the right order. + */ + private void resetPreferences() { + final int numberOfCategories = sCategoryInfos.length; + + removePreference(mUsageBarPreference); + for (int i = 0; i < numberOfCategories; i++) { + removePreference(mPreferences[i]); + } + removePreference(mMountTogglePreference); + removePreference(mFormatPreference); + + addPreference(mUsageBarPreference); + for (int i = 0; i < numberOfCategories; i++) { + addPreference(mPreferences[i]); + } + addPreference(mMountTogglePreference); + addPreference(mFormatPreference); + + mMountTogglePreference.setEnabled(true); + } + + private void updatePreferencesFromState() { + resetPreferences(); + + String state = getStorageVolumeState(); + + String readOnly = ""; + if (state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { + state = Environment.MEDIA_MOUNTED; + readOnly = mResources.getString(R.string.read_only); + removePreference(mFormatPreference); + } + + if (mStorageVolume.isEmulated()) { + removePreference(mFormatPreference); + } + + if (!mStorageVolume.isRemovable() && !state.equals(Environment.MEDIA_UNMOUNTED)) { + // This device has built-in storage that is not removable. + // There is no reason for the user to unmount it. + removePreference(mMountTogglePreference); + } + + if (state.equals(Environment.MEDIA_MOUNTED)) { + mPreferences[AVAILABLE].setSummary(mPreferences[AVAILABLE].getSummary() + readOnly); + + mMountTogglePreference.setEnabled(true); + mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject)); + mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary)); + } else { + if (state.equals(Environment.MEDIA_UNMOUNTED) || state.equals(Environment.MEDIA_NOFS) + || state.equals(Environment.MEDIA_UNMOUNTABLE)) { + mMountTogglePreference.setEnabled(true); + mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount)); + mMountTogglePreference.setSummary(mResources.getString(R.string.sd_mount_summary)); + } else { + mMountTogglePreference.setEnabled(false); + mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount)); + mMountTogglePreference.setSummary(mResources.getString(R.string.sd_insert_summary)); + } + + removePreference(mUsageBarPreference); + removePreference(mPreferences[TOTAL_SIZE]); + removePreference(mPreferences[AVAILABLE]); + removePreference(mFormatPreference); + } + } + + public void updateApproximate(long totalSize, long availSize) { + mPreferences[TOTAL_SIZE].setSummary(formatSize(totalSize)); + mPreferences[AVAILABLE].setSummary(formatSize(availSize)); + + final long usedSize = totalSize - availSize; + + mUsageBarPreference.clear(); + mUsageBarPreference.addEntry(usedSize / (float) totalSize, android.graphics.Color.GRAY); + mUsageBarPreference.commit(); + + updatePreferencesFromState(); + } + + public void updateExact(long totalSize, long availSize, long appsSize, long downloadsSize, + long miscSize, long[] mediaSizes) { + mUsageBarPreference.clear(); + + mPreferences[TOTAL_SIZE].setSummary(formatSize(totalSize)); + + updatePreference(appsSize, totalSize, APPLICATIONS); + + long totalMediaSize = 0; + for (int i = 0; i < sMediaCategories.length; i++) { + final int category = sMediaCategories[i].mCategory; + final long size = mediaSizes[i]; + updatePreference(size, totalSize, category); + totalMediaSize += size; + } + + updatePreference(downloadsSize, totalSize, 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, MISC); + + updatePreference(availSize, totalSize, AVAILABLE); + + mUsageBarPreference.commit(); + } + + private void updatePreference(long size, long totalSize, int category) { + if (size > 0) { + mPreferences[category].setSummary(formatSize(size)); + mUsageBarPreference.addEntry(size / (float) totalSize, mColors[category]); + } else { + removePreference(mPreferences[category]); + } + } + + private void measure() { + mMeasurement.invalidate(); + mMeasurement.measure(); + } + + public void onResume() { + mMeasurement.setReceiver(this); + measure(); + } + + public void onStorageStateChanged() { + measure(); + } + + public void onMediaScannerFinished() { + measure(); + } + + public void onPause() { + mMeasurement.cleanUp(); + } + + private static ShapeDrawable createRectShape(int width, int height, int color) { + ShapeDrawable shape = new ShapeDrawable(new RectShape()); + shape.setIntrinsicHeight(height); + shape.setIntrinsicWidth(width); + shape.getPaint().setColor(color); + return shape; + } + + private String formatSize(long size) { + return Formatter.formatFileSize(getContext(), size); + } + + @Override + public void updateApproximate(Bundle bundle) { + final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE); + message.setData(bundle); + mUpdateHandler.sendMessage(message); + } + + @Override + public void updateExact(Bundle bundle) { + final Message message = mUpdateHandler.obtainMessage(MSG_UI_UPDATE_EXACT); + message.setData(bundle); + mUpdateHandler.sendMessage(message); + } + + public boolean mountToggleClicked(Preference preference) { + return preference == mMountTogglePreference; + } + + public Intent intentForClick(Preference preference) { + Intent intent = null; + + // TODO The current "delete" story is not fully handled by the respective applications. + // When it is done, make sure the intent types below are correct. + // If that cannot be done, remove these intents. + if (preference == mFormatPreference) { + intent = new Intent(Intent.ACTION_VIEW); + intent.setClass(getContext(), com.android.settings.MediaFormat.class); + } else if (preference == mPreferences[APPLICATIONS]) { + intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE); + intent.setClass(getContext(), + com.android.settings.Settings.ManageApplicationsActivity.class); + } else if (preference == mPreferences[DOWNLOADS]) { + intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra( + DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true); + } else if (preference == mPreferences[MUSIC]) { + intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("audio/mp3"); + } else if (preference == mPreferences[DCIM]) { + intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/jpeg"); // TODO Create a Videos category, type = video/* + } else if (preference == mPreferences[MISC]) { + Context context = getContext().getApplicationContext(); + if (mMeasurement.getMiscSize() > 0) { + intent = new Intent(context, MiscFilesHandler.class); + intent.putExtra(STORAGE_VOLUME, mStorageVolume); + } + } + + return intent; + } +}