<color name="memory_avail">#333333</color>
<color name="memory_apps_usage">#77831A</color>
<color name="memory_downloads">#476093</color>
- <color name="memory_video">#793A7F</color>
- <color name="memory_audio">#8E562A</color>
+ <color name="memory_dcim">#793A7F</color>
+ <color name="memory_music">#8E562A</color>
<color name="memory_misc">#7C3030</color>
<color name="crypt_keeper_clock_background">#ff9a9a9a</color>
<string name="proxy_clear_text">Clear</string>
<!-- HTTP proxy settings. The port number label. -->
<string name="proxy_port_label">Proxy port</string>
- <!-- HTTP proxy settings. The hint text field for port. [CHAR LIMIT=50]-->
- <string name="proxy_port_hint">8080</string>
+ <!-- HTTP proxy settings. The hint text field for port. -->
+ <string name="proxy_port_hint" translatable="false">8080</string>
<!-- HTTP proxy settings. The exclusion list label. -->
<string name="proxy_exclusionlist_label">Bypass proxy for</string>
<!-- HTTP proxy settings. The hint text for proxy exclusion list. [CHAR LIMIT=50]-->
<string name="wifi_speed">Link speed</string>
<!-- Label for the IP address of the connection -->
<string name="wifi_ip_address">IP address</string>
- <!-- Hint text for the IP address [CHAR LIMIT=25]-->
- <string name="wifi_ip_address_hint">192.168.1.128</string>
+ <!-- Hint text for the IP address -->
+ <string name="wifi_ip_address_hint" translatable="false">192.168.1.128</string>
<!-- Label for the EAP method of the network -->
<string name="wifi_eap_method">EAP method</string>
<!-- Label for the phase2 -->
<string name="wifi_ip_settings_invalid_network_prefix_length">Please type a network prefix length between 0 and 32.</string>
<!-- Label for the DNS (first one) -->
<string name="wifi_dns1">DNS 1</string>
- <!-- Hint text for DNS [CHAR LIMIT=25]-->
- <string name="wifi_dns1_hint">8.8.8.8</string>
+ <!-- Hint text for DNS -->
+ <string name="wifi_dns1_hint" translatable="false">8.8.8.8</string>
<!-- Label for the DNS (second one)-->
<string name="wifi_dns2">DNS 2</string>
- <!-- Hint text for DNS [CHAR LIMIT=25]-->
- <string name="wifi_dns2_hint">4.4.4.4</string>
+ <!-- Hint text for DNS -->
+ <string name="wifi_dns2_hint" translatable="false">4.4.4.4</string>
<!-- Label for the gateway of the network -->
<string name="wifi_gateway">Gateway</string>
- <!-- Hint text for the gateway [CHAR LIMIT=25]-->
- <string name="wifi_gateway_hint">192.168.1.1</string>
+ <!-- Hint text for the gateway -->
+ <string name="wifi_gateway_hint" translatable="false">192.168.1.1</string>
<!-- Label for the network prefix of the network [CHAR LIMIT=25]-->
<string name="wifi_network_prefix_length">Network prefix length</string>
- <!-- Hint text for network prefix length [CHAR LIMIT=25] -->
- <string name="wifi_network_prefix_length_hint">24</string>
+ <!-- Hint text for network prefix length -->
+ <string name="wifi_network_prefix_length_hint" translatable="false">24</string>
<!-- Wifi AP settings-->
<!-- Label for wifi tether checkbox. Toggles Access Point on/off -->
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of media on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
<string name="memory_media_usage">Media</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of /sdcard/Download on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
- <string name="memory_downloads_usage">Downloads</string>
+ <string name="memory_downloads_usage">Downloads</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of all pictures, videos in /sdcard/DCIM, /sdcard/Pictures folders on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
<string name="memory_dcim_usage">Pictures, Videos</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of audio files in /sdcard on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
<!-- SD card & phone storage settings item title that will result in the phone mounting the SD card. -->
<string name="sd_mount" product="default">Mount SD card</string>
- <!-- Subtext for Mount USB storage in Storage settings. User should never see this. -->
- <string name="sd_mount_summary" product="nosdcard">Mount the USB storage</string>
- <!-- SD card & phone storage settings item title that will result in the phone mounting the SD card. -->
- <string name="sd_mount_summary" product="default">Mount the SD card</string>
+ <!-- Subtext for Mount USB storage in Storage settings. User should never see this since we use automount. -->
+ <string name="sd_mount_summary" product="nosdcard"></string>
+ <!-- Subtext for Mount SD Card in Storage settings. User should never see this since we use automount. -->
+ <string name="sd_mount_summary" product="default"></string>
<!-- SD card & phone storage settings item title that will result in the phone formatting the USB storage. [CHAR LIMIT=25] -->
<string name="sd_format" product="nosdcard">Erase USB storage</string>
<!-- SD card & phone storage settings item title that will result in the phone formatting the SD card. [CHAR LIMIT=25] -->
<string name="sd_format_summary" product="nosdcard">Erases all data on the internal USB storage, such as music and photos</string>
<!-- SD card & phone storage settings item title that will result in the phone unmounting the SD card. [CHAR LIMIT=80] -->
<string name="sd_format_summary" product="default">Erases all data on the SD card, such as music and photos</string>
- <!-- SD card status when it is not available status -->
- <string name="sd_unavailable">Unavailable</string>
- <!-- SD card status when it is mounted as read only -->
+ <!-- SD card status when it is mounted as read only. Will be appended to size, starts with an unbreakable space -->
<string name="read_only">\u0020(Read-only)</string>
<!-- SD card eject confirmation dialog title [CHAR LIMIT=25] -->
<string name="dlg_confirm_unmount_title" product="nosdcard">Unmount USB storage</string>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/storage_settings_title">
- <PreferenceCategory android:key="memory_sd"
- android:title="@string/sd_memory">
- <Preference android:key="memory_sd_size"
- style="?android:attr/preferenceInformationStyle"
- android:title="@string/memory_size"
- android:summary="00"/>
- <Preference android:key="memory_sd_avail"
- style="?android:attr/preferenceInformationStyle"
- android:title="@string/memory_available"
- android:summary="00"/>
- <Preference android:key="memory_sd_mount_toggle"
- android:title="@string/sd_eject"
- android:summary="@string/sd_eject_summary"/>
- <Preference android:key="memory_sd_format"
- android:title="@string/sd_format"
- android:summary="@string/sd_format_summary"/>
- </PreferenceCategory>
+<!-- Preference categories are dynamically created based on the list of available storage volumes -->
- <PreferenceCategory android:title="@string/internal_memory">
- <com.android.settings.deviceinfo.UsageBarPreference
- android:key="memory_internal_chart"/>
-
- <Preference android:key="memory_internal_size"
- android:title="@string/memory_size"
- android:summary="@string/memory_calculating_size"/>
-
- <Preference android:key="memory_internal_apps"
- android:title="@string/memory_apps_usage"
- android:summary="@string/memory_calculating_size"/>
-
- <Preference android:key="memory_internal_downloads"
- android:title="@string/memory_downloads_usage"
- android:summary="@string/memory_calculating_size"/>
-
- <Preference android:key="memory_internal_dcim"
- android:title="@string/memory_dcim_usage"
- android:summary="@string/memory_calculating_size"/>
-
- <Preference android:key="memory_internal_music"
- android:title="@string/memory_music_usage"
- android:summary="@string/memory_calculating_size"/>
-
- <Preference android:key="memory_internal_media_misc"
- android:title="@string/memory_media_misc_usage"
- android:summary="@string/memory_calculating_size"/>
-
- <Preference android:key="memory_internal_avail"
- android:title="@string/memory_available"
- android:summary="@string/memory_calculating_size"/>
- </PreferenceCategory>
</PreferenceScreen>
+++ /dev/null
-/*
- * 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<MediaDirectory> mMediaDirs = new ArrayList<MediaDirectory>();
- static final List<String> ExclusionTargetsForMiscFiles = new ArrayList<String>();
- 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");
- }
- }
-}
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;
-
- // External storage preferences
- private Preference mSdSize;
- private Preference mSdAvail;
- private Preference mSdMountToggle;
- private Preference mSdFormat;
- private PreferenceGroup mSdMountPreferenceGroup;
+ private Resources mResources;
- // 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) {
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;
- }
- 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;
+ 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].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() {
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;
+ }
}
}
};
@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
@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();
}
};
.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)
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) {
}
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<ApplicationInfo> list = am.getRunningExternalApplications();
if (list != null && list.size() > 0) {
return true;
}
+ */
return false;
}
// 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;
}
}
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;
}
}
+++ /dev/null
-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<MeasurementReceiver> mReceiver;
-
- // Internal memory fields
- private long mInternalTotalSize;
- private long mInternalAvailSize;
- private long mInternalAppsSize;
-
- // External memory fields
- private long mExternalTotalSize;
-
- private long mExternalAvailSize;
- List<FileInfo> 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<MeasurementReceiver>(receiver);
- }
- }
-
- public void measureExternal() {
- if (!mHandler.hasMessages(MeasurementHandler.MSG_MEASURE_EXTERNAL)) {
- mHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE_EXTERNAL);
- }
- }
-
- public void measureInternal() {
- if (!mHandler.hasMessages(MeasurementHandler.MSG_MEASURE_INTERNAL)) {
- mHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE_INTERNAL);
- }
- }
-
- public void cleanUp() {
- mReceiver = null;
- mHandler.cleanUp();
- }
-
- private void sendInternalApproximateUpdate() {
- MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
- if (receiver == null) {
- return;
- }
-
- Bundle bundle = new Bundle();
- bundle.putLong(TOTAL_SIZE, mInternalTotalSize);
- bundle.putLong(AVAIL_SIZE, mInternalAvailSize);
-
- receiver.updateApproximateInternal(bundle);
- }
-
- private void sendInternalExactUpdate() {
- MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
- if (receiver == null) {
- 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<Context> mContext;
-
- final private ServiceConnection mDefContainerConn = new ServiceConnection() {
- public void onServiceConnected(ComponentName name, IBinder service) {
- final IMediaContainerService imcs = IMediaContainerService.Stub
- .asInterface(service);
- mDefaultContainer = imcs;
- mBound = true;
- sendMessage(obtainMessage(MSG_CONNECTED, imcs));
- }
-
- public void onServiceDisconnected(ComponentName name) {
- mBound = false;
- removeMessages(MSG_CONNECTED);
- }
- };
-
- public MeasurementHandler(Context context, Looper looper) {
- super(looper);
- mContext = new WeakReference<Context>(context);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_MEASURE_EXTERNAL: {
- if (mMeasured) {
- sendExternalApproximateUpdate();
- break;
- }
-
- measureApproximateExternalStorage();
- break;
- }
- case MSG_MEASURE_INTERNAL: {
- if (mMeasured) {
- sendInternalExactUpdate();
- break;
- }
-
- final Context context = (mContext != null) ? mContext.get() : null;
- if (context == null) {
- return;
- }
-
- measureApproximateInternalStorage();
-
- synchronized (mLock) {
- if (mBound) {
- removeMessages(MSG_DISCONNECT);
- sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
- } else {
- Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
- context.bindService(service, mDefContainerConn,
- Context.BIND_AUTO_CREATE);
- }
- }
- break;
- }
- case MSG_CONNECTED: {
- IMediaContainerService imcs = (IMediaContainerService) msg.obj;
- measureExactInternalStorage(imcs);
- 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<String> 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<String> mAppsList = new ArrayList<String>();
- public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
- if (!mStatsObserver.equals(this)) {
- // this callback's class object is no longer in use. ignore this callback.
- return;
- }
- if (succeeded) {
- 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<String> 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<ApplicationInfo> 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<FileInfo>();
- 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<FileInfo> {
- 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;
- }
- }
-}
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;
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;
//item not selected
continue;
}
- if (MemoryMeasurement.LOGV) {
+ if (StorageMeasurement.LOGV) {
Log.i(TAG, "deleting: " + mAdapter.getItem(i));
}
// delete the file
}
public void onDestroyActionMode(ActionMode mode) {
+ // This block intentionally left blank
}
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
}
}
- public class MemoryMearurementAdapter extends BaseAdapter {
- private ArrayList<MemoryMeasurement.FileInfo> mData = null;
+ class MemoryMearurementAdapter extends BaseAdapter {
+ private ArrayList<StorageMeasurement.FileInfo> mData = null;
private long mDataSize = 0;
private Context mContext;
- public MemoryMearurementAdapter(Context context) {
- mContext = context;
- MemoryMeasurement mMeasurement = MemoryMeasurement.getInstance(context);
- mData = (ArrayList<MemoryMeasurement.FileInfo>)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<StorageMeasurement.FileInfo>) mMeasurement.mFileInfoForMisc;
if (mData != null) {
- for (MemoryMeasurement.FileInfo info : mData) {
+ for (StorageMeasurement.FileInfo info : mData) {
mDataSize += info.mSize;
}
}
}
@Override
- public MemoryMeasurement.FileInfo getItem(int position) {
+ public StorageMeasurement.FileInfo getItem(int position) {
if (mData == null || mData.size() <= position) {
return null;
}
}
return mData.get(position).mId;
}
+
public void removeAll(List<Object> objs) {
if (mData == null) {
return;
}
for (Object o : objs) {
mData.remove(o);
- mDataSize -= ((MemoryMeasurement.FileInfo) o).mSize;
+ mDataSize -= ((StorageMeasurement.FileInfo) o).mSize;
}
}
* Sets the background for this chart. Callers are responsible for later
* calling {@link #invalidate()}.
*/
+ @Override
public void setBackgroundColor(int color) {
mEmptyPaint.setColor(color);
}
--- /dev/null
+/*
+ * 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<StorageVolume, StorageMeasurement> sInstances =
+ new ConcurrentHashMap<StorageVolume, StorageMeasurement>();
+
+ private volatile WeakReference<MeasurementReceiver> 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<FileInfo> 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<MeasurementReceiver>(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<Context> mContext;
+
+ final private ServiceConnection mDefContainerConn = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IMediaContainerService imcs = IMediaContainerService.Stub
+ .asInterface(service);
+ mDefaultContainer = imcs;
+ mBound = true;
+ sendMessage(obtainMessage(MSG_CONNECTED, imcs));
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mBound = false;
+ removeMessages(MSG_CONNECTED);
+ }
+ };
+
+ public MeasurementHandler(Context context, Looper looper) {
+ super(looper);
+ mContext = new WeakReference<Context>(context);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_MEASURE: {
+ 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<String> mAppsList = new ArrayList<String>();
+
+ public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
+ if (!mStatsObserver.equals(this)) {
+ // this callback's class object is no longer in use. ignore this callback.
+ return;
+ }
+
+ if (succeeded) {
+ 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<ApplicationInfo> 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<FileInfo>();
+ 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<FileInfo> {
+ 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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<String> sPathsExcludedForMisc = new HashSet<String>();
+
+ 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;
+ }
+}