summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2015-04-11 21:27:33 -0700
committerJeff Sharkey <jsharkey@android.com>2015-04-12 22:02:32 -0700
commit42833b2ff4d7a26dd9a609d2fd4436d9a26f28b5 (patch)
tree18ffcc0f4eea720d6a98de6c3549600e6f7d1b53
parent09c0c1385a08c23b8063626d5f439088b27d9c52 (diff)
downloadpackages_apps_Settings-42833b2ff4d7a26dd9a609d2fd4436d9a26f28b5.zip
packages_apps_Settings-42833b2ff4d7a26dd9a609d2fd4436d9a26f28b5.tar.gz
packages_apps_Settings-42833b2ff4d7a26dd9a609d2fd4436d9a26f28b5.tar.bz2
Checkpoint of new storage UI.
Top-level storage UI now shows list of all devices, both internal and adopted/private volumes, and public/shared volumes. When viewing a private volume, show traditional clustering of data types, including summary of other users. For adopted volumes, any actions are tucked away in a menu, since they're not primary. Misc files browsing is now provided by DocumentsUI. Teach StorageMeasurement about new private volumes, including handling emulated volumes stacked above them. When measuring, only consider apps actually hosted on the current volume UUID. When viewing a public volume, we default to launching into file management mode, and offer a simple eject button at the top-level view. File management mode is offered by new DocumentsUI browse intent, and a Settings link there redirects back to us for actual operations like ejecting/formatting. When unmounted, we launch into our action view. Actions like ejecting/formatting just show simple toasts for now. Bug: 19993667 Change-Id: Ie990ef3c01fb3717aaf8c79bfc53aac7edefdcf7
-rw-r--r--AndroidManifest.xml22
-rw-r--r--res/layout/preference_storage_action.xml47
-rw-r--r--res/menu/storage_volume.xml33
-rw-r--r--res/values/strings.xml43
-rw-r--r--res/xml/dashboard_categories.xml2
-rw-r--r--res/xml/device_info_storage.xml31
-rw-r--r--res/xml/device_info_storage_volume.xml (renamed from res/xml/device_info_memory.xml)15
-rw-r--r--src/com/android/settings/Settings.java1
-rw-r--r--src/com/android/settings/SettingsActivity.java6
-rw-r--r--src/com/android/settings/deviceinfo/Memory.java509
-rw-r--r--src/com/android/settings/deviceinfo/MiscFilesHandler.java286
-rw-r--r--src/com/android/settings/deviceinfo/PrivateVolumeSettings.java548
-rw-r--r--src/com/android/settings/deviceinfo/PublicVolumeSettings.java205
-rw-r--r--src/com/android/settings/deviceinfo/StorageItemPreference.java4
-rw-r--r--src/com/android/settings/deviceinfo/StorageMeasurement.java302
-rw-r--r--src/com/android/settings/deviceinfo/StorageSettings.java421
-rw-r--r--src/com/android/settings/deviceinfo/StorageVolumePreference.java89
-rw-r--r--src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java483
-rw-r--r--src/com/android/settings/search/Ranking.java4
-rw-r--r--src/com/android/settings/search/SearchIndexableResources.java8
20 files changed, 1561 insertions, 1498 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2484831..66a081d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1415,15 +1415,31 @@
<category android:name="android.intent.category.VOICE_LAUNCH" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.deviceinfo.Memory" />
+ android:value="com.android.settings.deviceinfo.StorageSettings" />
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/storage_settings" />
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>
- <activity android:name=".deviceinfo.MiscFilesHandler"
- android:theme="@style/Theme.SubSettingsDialogWhenLarge"/>
+ <activity
+ android:name="Settings$StorageVolumeSettingsActivity"
+ android:label="@string/storage_settings_title"
+ android:taskAffinity="com.android.settings"
+ android:parentActivityName="Settings$StorageSettingsActivity">
+ <intent-filter>
+ <action android:name="android.provider.action.DOCUMENT_ROOT_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data
+ android:scheme="content"
+ android:host="com.android.externalstorage.documents"
+ android:mimeType="vnd.android.document/root" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.deviceinfo.PublicVolumeSettings" />
+ <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+ android:resource="@id/storage_settings" />
+ </activity>
<activity android:name="ApnEditor"
android:label="@string/apn_edit">
diff --git a/res/layout/preference_storage_action.xml b/res/layout/preference_storage_action.xml
new file mode 100644
index 0000000..10f138b
--- /dev/null
+++ b/res/layout/preference_storage_action.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/unmount"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:contentDescription="@string/storage_menu_unmount"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:textSize="30sp"
+ android:background="?android:attr/selectableItemBackground" />
+
+ <!--
+ <ImageView
+ android:id="@+id/eject"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:src="@drawable/ic_sync_green_holo"
+ android:contentDescription="@string/storage_menu_eject"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground" />
+ -->
+</LinearLayout>
diff --git a/res/menu/storage_volume.xml b/res/menu/storage_volume.xml
new file mode 100644
index 0000000..093a4bb
--- /dev/null
+++ b/res/menu/storage_volume.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/storage_rename"
+ android:title="@string/storage_menu_rename" />
+ <item
+ android:id="@+id/storage_mount"
+ android:title="@string/storage_menu_mount" />
+ <item
+ android:id="@+id/storage_unmount"
+ android:title="@string/storage_menu_unmount" />
+ <item
+ android:id="@+id/storage_format"
+ android:title="@string/storage_menu_format" />
+ <item
+ android:id="@+id/storage_usb"
+ android:title="@string/storage_menu_usb" />
+</menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a8f7bb5..a608410 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -593,6 +593,9 @@
<!-- Button label for generic forget action [CHAR LIMIT=20] -->
<string name="forget">Forget</string>
+ <!-- Button label for generic save action [CHAR LIMIT=20] -->
+ <string name="save">Save</string>
+
<!-- Title of the Settings activity shown within the application itself. -->
<string name="settings_label">Settings</string>
<!-- Title of the Settings activity shown in the Launcher. [CHAR LIMIT=20] -->
@@ -2185,7 +2188,7 @@
<!-- SD card & phone storage settings summary. Displayed when the total memory usage is being calculated. Will be replaced with a number like "12.3 GB" when finished calucating. [CHAR LIMIT=30] -->
<string name="memory_calculating_size">Calculating\u2026</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of applications installed. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
- <string name="memory_apps_usage">Apps (app data &amp; media content)</string>
+ <string name="memory_apps_usage">Apps &amp; app data</string>
<!-- 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] -->
@@ -2195,7 +2198,7 @@
<!-- 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] -->
<string name="memory_music_usage">Audio (music, ringtones, podcasts, etc.)</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of misc files on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
- <string name="memory_media_misc_usage">Misc.</string>
+ <string name="memory_media_misc_usage">Other files</string>
<!-- Storage item representing all cached data on device. [CHAR LIMIT=48] -->
<string name="memory_media_cache_usage">Cached data</string>
<!-- SD card & phone storage settings item title that will result in the phone unmounting the SD card. This will be done before the user phyiscally removes the SD card from the phone. Kind of like the "Safely remove" on some operating systems. [CHAR LIMIT=25] -->
@@ -2270,6 +2273,16 @@
<!-- Settings item summary when storage is running low [CHAR LIMIT=NONE] -->
<string name="storage_low_summary">Some system functions, such as syncing, may not work correctly. Try to free space by deleting or unpinning items, such as apps or media content.</string>
+ <!-- Storage setting. Menu option for renaming a storage device [CHAR LIMIT=30]-->
+ <string name="storage_menu_rename">Rename</string>
+ <!-- Storage setting. Menu option for mounting a storage device [CHAR LIMIT=30]-->
+ <string name="storage_menu_mount">Mount</string>
+ <!-- Storage setting. Menu option for unmounting a storage device [CHAR LIMIT=30]-->
+ <string name="storage_menu_unmount">Eject</string>
+ <!-- Storage setting. Menu option for erasing and formatting a storage device [CHAR LIMIT=30]-->
+ <string name="storage_menu_format">Erase &amp; format</string>
+ <!-- Storage setting. Menu option for erasing and formatting a storage device [CHAR LIMIT=30]-->
+ <string name="storage_menu_format_internal">Erase &amp; format as internal storage</string>
<!-- Storage setting. Menu option for USB transfer settings [CHAR LIMIT=30]-->
<string name="storage_menu_usb">USB computer connection</string>
@@ -2293,6 +2306,32 @@
<!-- Section header above list of other users storage [CHAR LIMIT=32] -->
<string name="storage_other_users">Other users</string>
+ <!-- Section header above list of internal storage devices [CHAR LIMIT=30]-->
+ <string name="storage_internal_title">Device storage</string>
+ <!-- Section header above list of external storage devices [CHAR LIMIT=30]-->
+ <string name="storage_external_title">Removable storage</string>
+
+ <!-- Summary of a single storage volume, constrasting available and total storage space. [CHAR LIMIT=48]-->
+ <string name="storage_volume_summary"><xliff:g id="free" example="1.2GB">%1$s</xliff:g> free (Total <xliff:g id="total" example="32GB">%2$s</xliff:g>)</string>
+
+ <!-- Toast informing that storage mount operation was successful. [CHAR LIMIT=64]-->
+ <string name="storage_mount_success"><xliff:g id="name" example="SD card">%1$s</xliff:g> is mounted</string>
+ <!-- Toast informing that storage unmount operation failed. [CHAR LIMIT=64]-->
+ <string name="storage_mount_failure">Couldn\'t mount <xliff:g id="name" example="SD card">%1$s</xliff:g></string>
+
+ <!-- Toast informing that storage unmount operation was successful. [CHAR LIMIT=64]-->
+ <string name="storage_unmount_success"><xliff:g id="name" example="SD card">%1$s</xliff:g> is safely ejected</string>
+ <!-- Toast informing that storage unmount operation failed. [CHAR LIMIT=64]-->
+ <string name="storage_unmount_failure">Couldn\'t safely eject <xliff:g id="name" example="SD card">%1$s</xliff:g></string>
+
+ <!-- Toast informing that storage format operation was successful. [CHAR LIMIT=64]-->
+ <string name="storage_format_success"><xliff:g id="name" example="SD card">%1$s</xliff:g> is formatted</string>
+ <!-- Toast informing that storage format operation failed. [CHAR LIMIT=64]-->
+ <string name="storage_format_failure">Couldn\'t format <xliff:g id="name" example="SD card">%1$s</xliff:g></string>
+
+ <!-- Title of dialog prompting user to rename a storage volume [CHAR LIMIT=32]-->
+ <string name="storage_rename_title">Rename storage</string>
+
<!-- Phone info screen, section titles: -->
<string name="battery_status_title">Battery status</string>
<!-- Phone info screen, section titles: -->
diff --git a/res/xml/dashboard_categories.xml b/res/xml/dashboard_categories.xml
index 0d8c1ac..78fe0e7 100644
--- a/res/xml/dashboard_categories.xml
+++ b/res/xml/dashboard_categories.xml
@@ -106,7 +106,7 @@
<dashboard-tile
android:id="@+id/storage_settings"
android:title="@string/storage_settings"
- android:fragment="com.android.settings.deviceinfo.Memory"
+ android:fragment="com.android.settings.deviceinfo.StorageSettings"
android:icon="@drawable/ic_settings_storage"
/>
diff --git a/res/xml/device_info_storage.xml b/res/xml/device_info_storage.xml
new file mode 100644
index 0000000..19c5982
--- /dev/null
+++ b/res/xml/device_info_storage.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+ android:title="@string/storage_settings"
+ settings:keywords="@string/keywords_storage">
+
+ <PreferenceCategory
+ android:key="storage_internal"
+ android:title="@string/storage_internal_title" />
+
+ <PreferenceCategory
+ android:key="storage_external"
+ android:title="@string/storage_external_title" />
+
+</PreferenceScreen>
diff --git a/res/xml/device_info_memory.xml b/res/xml/device_info_storage_volume.xml
index 38cc94a..35435d7 100644
--- a/res/xml/device_info_memory.xml
+++ b/res/xml/device_info_storage_volume.xml
@@ -4,9 +4,9 @@
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.
@@ -14,11 +14,10 @@
limitations under the License.
-->
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
- android:title="@string/storage_settings"
- settings:keywords="@string/keywords_storage">
-
-<!-- Preference categories are dynamically created based on the list of available storage volumes -->
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+ android:title="@string/storage_settings"
+ settings:keywords="@string/keywords_storage">
</PreferenceScreen>
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index b58159e..fe0df59 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -33,6 +33,7 @@ public class Settings extends SettingsActivity {
public static class VpnSettingsActivity extends SettingsActivity { /* empty */ }
public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ }
public static class StorageSettingsActivity extends SettingsActivity { /* empty */ }
+ public static class StorageVolumeSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ }
public static class InputMethodAndLanguageSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 24209b0..7bfd249 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -82,7 +82,8 @@ import com.android.settings.dashboard.DashboardSummary;
import com.android.settings.dashboard.DashboardTile;
import com.android.settings.dashboard.NoHomeDialogFragment;
import com.android.settings.dashboard.SearchResultsSummary;
-import com.android.settings.deviceinfo.Memory;
+import com.android.settings.deviceinfo.PublicVolumeSettings;
+import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.deviceinfo.UsbSettings;
import com.android.settings.fuelgauge.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
@@ -305,7 +306,8 @@ public class SettingsActivity extends Activity
CaptionPropertiesFragment.class.getName(),
com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
TextToSpeechSettings.class.getName(),
- Memory.class.getName(),
+ StorageSettings.class.getName(),
+ PublicVolumeSettings.class.getName(),
DevelopmentSettings.class.getName(),
UsbSettings.class.getName(),
AndroidBeam.class.getName(),
diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java
deleted file mode 100644
index a07f7c2..0000000
--- a/src/com/android/settings/deviceinfo/Memory.java
+++ /dev/null
@@ -1,509 +0,0 @@
-/*
- * Copyright (C) 2008 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.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.IPackageDataObserver;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.hardware.usb.UsbManager;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserManager;
-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.PreferenceScreen;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.widget.Toast;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.Utils;
-import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableRaw;
-import com.google.android.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Panel showing storage usage on disk for known {@link StorageVolume} returned
- * by {@link StorageManager}. Calculates and displays usage of data types.
- */
-public class Memory extends SettingsPreferenceFragment implements Indexable {
- private static final String TAG = "MemorySettings";
-
- private static final String TAG_CONFIRM_CLEAR_CACHE = "confirmClearCache";
-
- private static final int DLG_CONFIRM_UNMOUNT = 1;
- private static final int DLG_ERROR_UNMOUNT = 2;
-
- // The mountToggle Preference that has last been clicked.
- // Assumes no two successive unmount event on 2 different volumes are performed before the first
- // one's preference is disabled
- private static Preference sLastClickedMountToggle;
- private static String sClickedMountPoint;
-
- // Access using getMountService()
- private IMountService mMountService;
- private StorageManager mStorageManager;
- private UsbManager mUsbManager;
-
- private ArrayList<StorageVolumePreferenceCategory> mCategories = Lists.newArrayList();
-
- @Override
- protected int getMetricsCategory() {
- return MetricsLogger.DEVICEINFO_MEMORY;
- }
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- final Context context = getActivity();
-
- mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
-
- mStorageManager = StorageManager.from(context);
- mStorageManager.registerListener(mStorageListener);
-
- addPreferencesFromResource(R.xml.device_info_memory);
-
- addCategory(StorageVolumePreferenceCategory.buildForInternal(context));
-
- final StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
- for (StorageVolume volume : storageVolumes) {
- if (!volume.isEmulated()) {
- addCategory(StorageVolumePreferenceCategory.buildForPhysical(context, volume));
- }
- }
-
- setHasOptionsMenu(true);
- }
-
- private void addCategory(StorageVolumePreferenceCategory category) {
- mCategories.add(category);
- getPreferenceScreen().addPreference(category);
- category.init();
- }
-
- private boolean isMassStorageEnabled() {
- // Mass storage is enabled if primary volume supports it
- final StorageVolume[] volumes = mStorageManager.getVolumeList();
- final StorageVolume primary = StorageManager.getPrimaryVolume(volumes);
- return primary != null && primary.allowMassStorage();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
- intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
- intentFilter.addDataScheme("file");
- getActivity().registerReceiver(mMediaScannerReceiver, intentFilter);
-
- intentFilter = new IntentFilter();
- intentFilter.addAction(UsbManager.ACTION_USB_STATE);
- getActivity().registerReceiver(mMediaScannerReceiver, intentFilter);
-
- for (StorageVolumePreferenceCategory category : mCategories) {
- category.onResume();
- }
- }
-
- StorageEventListener mStorageListener = new StorageEventListener() {
- @Override
- public void onStorageStateChanged(String path, String oldState, String newState) {
- Log.i(TAG, "Received storage state changed notification that " + path +
- " changed state from " + oldState + " to " + newState);
- for (StorageVolumePreferenceCategory category : mCategories) {
- final StorageVolume volume = category.getStorageVolume();
- if (volume != null && path.equals(volume.getPath())) {
- category.onStorageStateChanged();
- break;
- }
- }
- }
- };
-
- @Override
- public void onPause() {
- super.onPause();
- getActivity().unregisterReceiver(mMediaScannerReceiver);
- for (StorageVolumePreferenceCategory category : mCategories) {
- category.onPause();
- }
- }
-
- @Override
- public void onDestroy() {
- if (mStorageManager != null && mStorageListener != null) {
- mStorageManager.unregisterListener(mStorageListener);
- }
- super.onDestroy();
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.storage, menu);
- }
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- final MenuItem usb = menu.findItem(R.id.storage_usb);
- UserManager um = (UserManager)getActivity().getSystemService(Context.USER_SERVICE);
- boolean usbItemVisible = !isMassStorageEnabled()
- && !um.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
- usb.setVisible(usbItemVisible);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.storage_usb:
- if (getActivity() instanceof SettingsActivity) {
- ((SettingsActivity) getActivity()).startPreferencePanel(
- UsbSettings.class.getCanonicalName(),
- null, R.string.storage_title_usb, null, this, 0);
- } else {
- startFragment(this, UsbSettings.class.getCanonicalName(),
- R.string.storage_title_usb, -1, null);
- }
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- private synchronized IMountService getMountService() {
- if (mMountService == null) {
- IBinder service = ServiceManager.getService("mount");
- if (service != null) {
- mMountService = IMountService.Stub.asInterface(service);
- } else {
- Log.e(TAG, "Can't get mount service");
- }
- }
- return mMountService;
- }
-
- @Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
- if (StorageVolumePreferenceCategory.KEY_CACHE.equals(preference.getKey())) {
- ConfirmClearCacheFragment.show(this);
- return true;
- }
-
- for (StorageVolumePreferenceCategory category : mCategories) {
- Intent intent = category.intentForClick(preference);
- if (intent != null) {
- // Don't go across app boundary if monkey is running
- if (!Utils.isMonkeyRunning()) {
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException anfe) {
- Log.w(TAG, "No activity found for intent " + intent);
- }
- }
- return true;
- }
-
- final StorageVolume volume = category.getStorageVolume();
- if (volume != null && category.mountToggleClicked(preference)) {
- sLastClickedMountToggle = preference;
- sClickedMountPoint = volume.getPath();
- String state = mStorageManager.getVolumeState(volume.getPath());
- if (Environment.MEDIA_MOUNTED.equals(state) ||
- Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
- unmount();
- } else {
- mount();
- }
- return true;
- }
- }
-
- return false;
- }
-
- private final BroadcastReceiver mMediaScannerReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(UsbManager.ACTION_USB_STATE)) {
- boolean isUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
- String usbFunction = mUsbManager.getDefaultFunction();
- for (StorageVolumePreferenceCategory category : mCategories) {
- category.onUsbStateChanged(isUsbConnected, usbFunction);
- }
- } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
- for (StorageVolumePreferenceCategory category : mCategories) {
- category.onMediaScannerFinished();
- }
- }
- }
- };
-
- @Override
- public Dialog onCreateDialog(int id) {
- switch (id) {
- case DLG_CONFIRM_UNMOUNT:
- return new AlertDialog.Builder(getActivity())
- .setTitle(R.string.dlg_confirm_unmount_title)
- .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- doUnmount();
- }})
- .setNegativeButton(R.string.cancel, null)
- .setMessage(R.string.dlg_confirm_unmount_text)
- .create();
- case DLG_ERROR_UNMOUNT:
- return new AlertDialog.Builder(getActivity())
- .setTitle(R.string.dlg_error_unmount_title)
- .setNeutralButton(R.string.dlg_ok, null)
- .setMessage(R.string.dlg_error_unmount_text)
- .create();
- }
- return null;
- }
-
- private void doUnmount() {
- // Present a toast here
- Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
- IMountService mountService = getMountService();
- try {
- sLastClickedMountToggle.setEnabled(false);
- sLastClickedMountToggle.setTitle(getString(R.string.sd_ejecting_title));
- sLastClickedMountToggle.setSummary(getString(R.string.sd_ejecting_summary));
- mountService.unmountVolume(sClickedMountPoint, true, false);
- } catch (RemoteException e) {
- // Informative dialog to user that unmount failed.
- showDialogInner(DLG_ERROR_UNMOUNT);
- }
- }
-
- private void showDialogInner(int id) {
- removeDialog(id);
- showDialog(id);
- }
-
- private boolean hasAppsAccessingStorage() throws RemoteException {
- IMountService mountService = getMountService();
- int stUsers[] = mountService.getStorageUsers(sClickedMountPoint);
- 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;
- }
- */
- // Better safe than sorry. Assume the storage is used to ask for confirmation.
- return true;
- }
-
- private void unmount() {
- // Check if external media is in use.
- try {
- if (hasAppsAccessingStorage()) {
- // Present dialog to user
- showDialogInner(DLG_CONFIRM_UNMOUNT);
- } else {
- doUnmount();
- }
- } catch (RemoteException e) {
- // Very unlikely. But present an error dialog anyway
- Log.e(TAG, "Is MountService running?");
- showDialogInner(DLG_ERROR_UNMOUNT);
- }
- }
-
- private void mount() {
- IMountService mountService = getMountService();
- try {
- if (mountService != null) {
- mountService.mountVolume(sClickedMountPoint);
- } else {
- Log.e(TAG, "Mount service is null, can't mount");
- }
- } catch (RemoteException ex) {
- // Not much can be done
- }
- }
-
- private void onCacheCleared() {
- for (StorageVolumePreferenceCategory category : mCategories) {
- category.onCacheCleared();
- }
- }
-
- private static class ClearCacheObserver extends IPackageDataObserver.Stub {
- private final Memory mTarget;
- private int mRemaining;
-
- public ClearCacheObserver(Memory target, int remaining) {
- mTarget = target;
- mRemaining = remaining;
- }
-
- @Override
- public void onRemoveCompleted(final String packageName, final boolean succeeded) {
- synchronized (this) {
- if (--mRemaining == 0) {
- mTarget.onCacheCleared();
- }
- }
- }
- }
-
- /**
- * Dialog to request user confirmation before clearing all cache data.
- */
- public static class ConfirmClearCacheFragment extends DialogFragment {
- public static void show(Memory parent) {
- if (!parent.isAdded()) return;
-
- final ConfirmClearCacheFragment dialog = new ConfirmClearCacheFragment();
- dialog.setTargetFragment(parent, 0);
- dialog.show(parent.getFragmentManager(), TAG_CONFIRM_CLEAR_CACHE);
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Context context = getActivity();
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.memory_clear_cache_title);
- builder.setMessage(getString(R.string.memory_clear_cache_message));
-
- builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final Memory target = (Memory) getTargetFragment();
- final PackageManager pm = context.getPackageManager();
- final List<PackageInfo> infos = pm.getInstalledPackages(0);
- final ClearCacheObserver observer = new ClearCacheObserver(
- target, infos.size());
- for (PackageInfo info : infos) {
- pm.deleteApplicationCacheFiles(info.packageName, observer);
- }
- }
- });
- builder.setNegativeButton(android.R.string.cancel, null);
-
- return builder.create();
- }
- }
-
- /**
- * Enable indexing of searchable data
- */
- public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider() {
- @Override
- public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
- final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
-
- SearchIndexableRaw data = new SearchIndexableRaw(context);
- data.title = context.getString(R.string.storage_settings);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
-
- data = new SearchIndexableRaw(context);
- data.title = context.getString(R.string.internal_storage);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
-
- data = new SearchIndexableRaw(context);
- final StorageVolume[] storageVolumes = StorageManager.from(context).getVolumeList();
- for (StorageVolume volume : storageVolumes) {
- if (!volume.isEmulated()) {
- data.title = volume.getDescription(context);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
- }
- }
-
- data = new SearchIndexableRaw(context);
- data.title = context.getString(R.string.memory_size);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
-
- data = new SearchIndexableRaw(context);
- data.title = context.getString(R.string.memory_available);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
-
- data = new SearchIndexableRaw(context);
- data.title = context.getString(R.string.memory_apps_usage);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
-
- data = new SearchIndexableRaw(context);
- data.title = context.getString(R.string.memory_dcim_usage);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
-
- data = new SearchIndexableRaw(context);
- data.title = context.getString(R.string.memory_music_usage);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
-
- data = new SearchIndexableRaw(context);
- data.title = context.getString(R.string.memory_downloads_usage);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
-
- data = new SearchIndexableRaw(context);
- data.title = context.getString(R.string.memory_media_cache_usage);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
-
- data = new SearchIndexableRaw(context);
- data.title = context.getString(R.string.memory_media_misc_usage);
- data.screenTitle = context.getString(R.string.storage_settings);
- result.add(data);
-
- return result;
- }
- };
-
-}
diff --git a/src/com/android/settings/deviceinfo/MiscFilesHandler.java b/src/com/android/settings/deviceinfo/MiscFilesHandler.java
deleted file mode 100644
index 93e352b..0000000
--- a/src/com/android/settings/deviceinfo/MiscFilesHandler.java
+++ /dev/null
@@ -1,286 +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.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.view.ActionMode;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-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;
-
-/**
- * This class handles the selection and removal of Misc files.
- */
-public class MiscFilesHandler extends ListActivity {
- private static final String TAG = "MemorySettings";
- private String mNumSelectedFormat;
- private String mNumBytesSelectedFormat;
- private MemoryMearurementAdapter mAdapter;
- private LayoutInflater mInflater;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setFinishOnTouchOutside(true);
- setTitle(R.string.misc_files);
- mNumSelectedFormat = getString(R.string.misc_files_selected_count);
- mNumBytesSelectedFormat = getString(R.string.misc_files_selected_count_bytes);
- mAdapter = new MemoryMearurementAdapter(this);
- mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- setContentView(R.layout.settings_storage_miscfiles_list);
- ListView lv = getListView();
- lv.setItemsCanFocus(true);
- lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
- lv.setMultiChoiceModeListener(new ModeCallback(this));
- setListAdapter(mAdapter);
- }
-
- private class ModeCallback implements ListView.MultiChoiceModeListener {
- private int mDataCount;
- private final Context mContext;
-
- public ModeCallback(Context context) {
- mContext = context;
- mDataCount = mAdapter.getCount();
- }
-
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- final MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.misc_files_menu, menu);
- return true;
- }
-
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return true;
- }
-
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- ListView lv = getListView();
- switch (item.getItemId()) {
- case R.id.action_delete:
- // delete the files selected
- SparseBooleanArray checkedItems = lv.getCheckedItemPositions();
- int checkedCount = getListView().getCheckedItemCount();
- if (checkedCount > mDataCount) {
- throw new IllegalStateException("checked item counts do not match. " +
- "checkedCount: " + checkedCount + ", dataSize: " + mDataCount);
- }
- if (mDataCount > 0) {
- ArrayList<Object> toRemove = new ArrayList<Object>();
- for (int i = 0; i < mDataCount; i++) {
- if (!checkedItems.get(i)) {
- //item not selected
- continue;
- }
- if (StorageMeasurement.LOGV) {
- Log.i(TAG, "deleting: " + mAdapter.getItem(i));
- }
- // delete the file
- File file = new File(mAdapter.getItem(i).mFileName);
- if (file.isDirectory()) {
- deleteDir(file);
- } else {
- file.delete();
- }
- toRemove.add(mAdapter.getItem(i));
- }
- mAdapter.removeAll(toRemove);
- mAdapter.notifyDataSetChanged();
- mDataCount = mAdapter.getCount();
- }
- mode.finish();
- break;
-
- case R.id.action_select_all:
- // check ALL items
- for (int i = 0; i < mDataCount; i++) {
- lv.setItemChecked(i, true);
- }
- // update the title and subtitle with number selected and numberBytes selected
- onItemCheckedStateChanged(mode, 1, 0, true);
- break;
- }
- return true;
- }
-
- // Deletes all files and subdirectories under given dir.
- // Returns true if all deletions were successful.
- // If a deletion fails, the method stops attempting to delete and returns false.
- private boolean deleteDir(File dir) {
- String[] children = dir.list();
- if (children != null) {
- for (int i=0; i < children.length; i++) {
- boolean success = deleteDir(new File(dir, children[i]));
- if (!success) {
- return false;
- }
- }
- }
- // The directory is now empty so delete it
- return dir.delete();
- }
-
- public void onDestroyActionMode(ActionMode mode) {
- // This block intentionally left blank
- }
-
- public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
- boolean checked) {
- ListView lv = getListView();
- int numChecked = lv.getCheckedItemCount();
- mode.setTitle(String.format(mNumSelectedFormat, numChecked, mAdapter.getCount()));
-
- // total the sizes of all items selected so far
- SparseBooleanArray checkedItems = lv.getCheckedItemPositions();
- long selectedDataSize = 0;
- if (numChecked > 0) {
- for (int i = 0; i < mDataCount; i++) {
- if (checkedItems.get(i)) {
- // item is checked
- selectedDataSize += mAdapter.getItem(i).mSize;
- }
- }
- }
- mode.setSubtitle(String.format(mNumBytesSelectedFormat,
- Formatter.formatFileSize(mContext, selectedDataSize),
- Formatter.formatFileSize(mContext, mAdapter.getDataSize())));
- }
- }
-
- class MemoryMearurementAdapter extends BaseAdapter {
- private ArrayList<StorageMeasurement.FileInfo> mData = null;
- private long mDataSize = 0;
- private Context mContext;
-
- public MemoryMearurementAdapter(Activity activity) {
- mContext = activity;
- final StorageVolume storageVolume = activity.getIntent().getParcelableExtra(
- StorageVolume.EXTRA_STORAGE_VOLUME);
- StorageMeasurement mMeasurement = StorageMeasurement.getInstance(
- activity, storageVolume);
- if (mMeasurement == null) return;
- mData = (ArrayList<StorageMeasurement.FileInfo>) mMeasurement.mFileInfoForMisc;
- if (mData != null) {
- for (StorageMeasurement.FileInfo info : mData) {
- mDataSize += info.mSize;
- }
- }
- }
-
- @Override
- public int getCount() {
- return (mData == null) ? 0 : mData.size();
- }
-
- @Override
- public StorageMeasurement.FileInfo getItem(int position) {
- if (mData == null || mData.size() <= position) {
- return null;
- }
- return mData.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- if (mData == null || mData.size() <= position) {
- return 0;
- }
- return mData.get(position).mId;
- }
-
- public void removeAll(List<Object> objs) {
- if (mData == null) {
- return;
- }
- for (Object o : objs) {
- mData.remove(o);
- mDataSize -= ((StorageMeasurement.FileInfo) o).mSize;
- }
- }
-
- public long getDataSize() {
- return mDataSize;
- }
-
- @Override
- public void notifyDataSetChanged() {
- super.notifyDataSetChanged();
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final FileItemInfoLayout view = (convertView == null) ?
- (FileItemInfoLayout) mInflater.inflate(R.layout.settings_storage_miscfiles,
- parent, false) : (FileItemInfoLayout) convertView;
- FileInfo item = getItem(position);
- view.setFileName(item.mFileName);
- view.setFileSize(Formatter.formatFileSize(mContext, item.mSize));
- final ListView listView = (ListView) parent;
- final int listPosition = position;
- view.getCheckBox().setOnCheckedChangeListener(new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- listView.setItemChecked(listPosition, isChecked);
- }
-
- });
- view.setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- if (listView.getCheckedItemCount() > 0) {
- return false;
- }
- listView.setItemChecked(listPosition, !view.isChecked());
- return true;
- }
- });
- view.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (listView.getCheckedItemCount() > 0) {
- listView.setItemChecked(listPosition, !view.isChecked());
- }
- }
- });
- return view;
- }
- }
-}
diff --git a/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java b/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java
new file mode 100644
index 0000000..efb9a07
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2015 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 static com.android.settings.deviceinfo.StorageSettings.EXTRA_VOLUME_ID;
+import static com.android.settings.deviceinfo.StorageSettings.TAG;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.DownloadManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.text.format.Formatter;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.EditText;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.Preconditions;
+import com.android.settings.R;
+import com.android.settings.Settings;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.deviceinfo.StorageMeasurement.MeasurementDetails;
+import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
+import com.android.settings.deviceinfo.StorageSettings.FormatTask;
+import com.android.settings.deviceinfo.StorageSettings.MountTask;
+import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
+import com.google.android.collect.Lists;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Panel showing summary and actions for a {@link VolumeInfo#TYPE_PRIVATE}
+ * storage volume.
+ */
+public class PrivateVolumeSettings extends SettingsPreferenceFragment {
+ // TODO: disable unmount when providing over MTP/PTP
+
+ private static final String TAG_RENAME = "rename";
+ private static final String TAG_CONFIRM_CLEAR_CACHE = "confirmClearCache";
+
+ private StorageManager mStorageManager;
+ private UserManager mUserManager;
+
+ private VolumeInfo mVolume;
+ private VolumeInfo mSharedVolume;
+
+ private StorageMeasurement mMeasure;
+
+ private UserInfo mCurrentUser;
+
+ private int mNextOrder = 0;
+
+ private UsageBarPreference mGraph;
+ private StorageItemPreference mTotal;
+ private StorageItemPreference mAvailable;
+ private StorageItemPreference mApps;
+ private StorageItemPreference mDcim;
+ private StorageItemPreference mMusic;
+ private StorageItemPreference mDownloads;
+ private StorageItemPreference mCache;
+ private StorageItemPreference mMisc;
+ private List<StorageItemPreference> mUsers = Lists.newArrayList();
+
+ private long mTotalSize;
+ private long mAvailSize;
+
+ @Override
+ protected int getMetricsCategory() {
+ return MetricsLogger.DEVICEINFO_STORAGE;
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ final Context context = getActivity();
+
+ mUserManager = context.getSystemService(UserManager.class);
+ mStorageManager = context.getSystemService(StorageManager.class);
+
+ final String volId = getArguments().getString(EXTRA_VOLUME_ID);
+ mVolume = Preconditions.checkNotNull(mStorageManager.findVolumeById(volId));
+ Preconditions.checkState(mVolume.type == VolumeInfo.TYPE_PRIVATE);
+
+ addPreferencesFromResource(R.xml.device_info_storage_volume);
+
+ // Find the emulated shared storage layered above this private volume
+ mSharedVolume = mStorageManager.findVolumeById(
+ mVolume.id.replace("private", "emulated"));
+
+ mMeasure = new StorageMeasurement(context, mVolume, mSharedVolume);
+ mMeasure.setReceiver(mReceiver);
+
+ mGraph = buildGraph();
+ mTotal = buildItem(R.string.memory_size, 0);
+ mAvailable = buildItem(R.string.memory_available, R.color.memory_avail);
+
+ mApps = buildItem(R.string.memory_apps_usage, R.color.memory_apps_usage);
+ mDcim = buildItem(R.string.memory_dcim_usage, R.color.memory_dcim);
+ mMusic = buildItem(R.string.memory_music_usage, R.color.memory_music);
+ mDownloads = buildItem(R.string.memory_downloads_usage, R.color.memory_downloads);
+ mCache = buildItem(R.string.memory_media_cache_usage, R.color.memory_cache);
+ mMisc = buildItem(R.string.memory_media_misc_usage, R.color.memory_misc);
+
+ mCurrentUser = mUserManager.getUserInfo(UserHandle.myUserId());
+ final List<UserInfo> otherUsers = getUsersExcluding(mCurrentUser);
+ for (int i = 0; i < otherUsers.size(); i++) {
+ final UserInfo user = otherUsers.get(i);
+ final int colorRes = i % 2 == 0 ? R.color.memory_user_light
+ : R.color.memory_user_dark;
+ final StorageItemPreference userPref = new StorageItemPreference(
+ context, user.name, colorRes, user.id);
+ mUsers.add(userPref);
+ }
+
+ setHasOptionsMenu(true);
+ }
+
+ public void refresh() {
+ getActivity().setTitle(mStorageManager.getBestVolumeDescription(mVolume.id));
+
+ // Valid options may have changed
+ getFragmentManager().invalidateOptionsMenu();
+
+ final Context context = getActivity();
+ final PreferenceScreen screen = getPreferenceScreen();
+
+ screen.removeAll();
+
+ if (mVolume.state != VolumeInfo.STATE_MOUNTED) {
+ return;
+ }
+
+ screen.addPreference(mGraph);
+ screen.addPreference(mTotal);
+ screen.addPreference(mAvailable);
+
+ final boolean showUsers = !mUsers.isEmpty();
+ if (showUsers) {
+ screen.addPreference(new PreferenceHeader(context, mCurrentUser.name));
+ }
+
+ screen.addPreference(mApps);
+ screen.addPreference(mDcim);
+ screen.addPreference(mMusic);
+ screen.addPreference(mDownloads);
+ screen.addPreference(mCache);
+ screen.addPreference(mMisc);
+
+ if (showUsers) {
+ screen.addPreference(new PreferenceHeader(context, R.string.storage_other_users));
+ for (Preference pref : mUsers) {
+ screen.addPreference(pref);
+ }
+ }
+
+ for (int i = 0; i < screen.getPreferenceCount(); i++) {
+ final Preference pref = screen.getPreference(i);
+ if (pref instanceof StorageItemPreference) {
+ ((StorageItemPreference) pref).setLoading();
+ }
+ }
+
+ final File file = new File(mVolume.path);
+ mTotalSize = file.getTotalSpace();
+ mAvailSize = file.getFreeSpace();
+
+ mTotal.setSummary(Formatter.formatFileSize(context, mTotalSize));
+ mAvailable.setSummary(Formatter.formatFileSize(context, mAvailSize));
+
+ mGraph.clear();
+ mGraph.addEntry(0, (mTotalSize - mAvailSize) / (float) mTotalSize,
+ android.graphics.Color.GRAY);
+ mGraph.commit();
+
+ mMeasure.forceMeasure();
+ }
+
+ private UsageBarPreference buildGraph() {
+ final UsageBarPreference pref = new UsageBarPreference(getActivity());
+ pref.setOrder(mNextOrder++);
+ return pref;
+ }
+
+ private StorageItemPreference buildItem(int titleRes, int colorRes) {
+ final StorageItemPreference pref = new StorageItemPreference(getActivity(), titleRes,
+ colorRes);
+ pref.setOrder(mNextOrder++);
+ return pref;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mStorageManager.registerListener(mStorageListener);
+ refresh();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mStorageManager.unregisterListener(mStorageListener);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mMeasure.onDestroy();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.storage_volume, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ final MenuItem rename = menu.findItem(R.id.storage_rename);
+ final MenuItem mount = menu.findItem(R.id.storage_mount);
+ final MenuItem unmount = menu.findItem(R.id.storage_unmount);
+ final MenuItem format = menu.findItem(R.id.storage_format);
+ final MenuItem usb = menu.findItem(R.id.storage_usb);
+
+ // Actions live in menu for non-internal private volumes; they're shown
+ // as preference items for public volumes.
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(mVolume.id)) {
+ rename.setVisible(false);
+ mount.setVisible(false);
+ unmount.setVisible(false);
+ format.setVisible(false);
+ } else {
+ rename.setVisible(mVolume.type == VolumeInfo.TYPE_PRIVATE);
+ mount.setVisible(mVolume.state == VolumeInfo.STATE_UNMOUNTED);
+ unmount.setVisible(mVolume.state == VolumeInfo.STATE_MOUNTED);
+ format.setVisible(true);
+ }
+
+ // TODO: show usb if we jumped past first screen
+ usb.setVisible(false);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final Context context = getActivity();
+ switch (item.getItemId()) {
+ case R.id.storage_rename:
+ RenameFragment.show(this);
+ return true;
+ case R.id.storage_mount:
+ new MountTask(context, mVolume.id).execute();
+ return true;
+ case R.id.storage_unmount:
+ new UnmountTask(context, mVolume.id).execute();
+ return true;
+ case R.id.storage_format:
+ new FormatTask(context, mVolume.id).execute();
+ return true;
+ case R.id.storage_usb:
+ startFragment(this, UsbSettings.class.getCanonicalName(),
+ R.string.storage_title_usb, 0, null);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference pref) {
+ // TODO: launch better intents for specific volume
+
+ Intent intent = null;
+ if (pref == mApps) {
+ intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
+ intent.setClass(getActivity(), Settings.ManageApplicationsActivity.class);
+
+ } else if (pref == mDownloads) {
+ intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra(
+ DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
+
+ } else if (pref == mMusic) {
+ intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("audio/mp3");
+
+ } else if (pref == mDcim) {
+ intent = new Intent(Intent.ACTION_VIEW);
+ intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
+ intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+
+ } else if (pref == mCache) {
+ ConfirmClearCacheFragment.show(this);
+ return true;
+
+ } else if (pref == mMisc) {
+ intent = StorageSettings.buildBrowseIntent(mSharedVolume);
+ }
+
+ if (intent != null) {
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "No activity found for " + intent);
+ }
+ return true;
+ }
+ return super.onPreferenceTreeClick(preferenceScreen, pref);
+ }
+
+ private final MeasurementReceiver mReceiver = new MeasurementReceiver() {
+ @Override
+ public void onDetailsChanged(MeasurementDetails details) {
+ updateDetails(details);
+ }
+ };
+
+ private void updateDetails(MeasurementDetails details) {
+ mGraph.clear();
+
+ updatePreference(mApps, details.appsSize);
+
+ final long dcimSize = totalValues(details.mediaSize, Environment.DIRECTORY_DCIM,
+ Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES);
+ updatePreference(mDcim, dcimSize);
+
+ final long musicSize = totalValues(details.mediaSize, Environment.DIRECTORY_MUSIC,
+ Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
+ Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
+ updatePreference(mMusic, musicSize);
+
+ final long downloadsSize = totalValues(details.mediaSize, Environment.DIRECTORY_DOWNLOADS);
+ updatePreference(mDownloads, downloadsSize);
+
+ updatePreference(mCache, details.cacheSize);
+ updatePreference(mMisc, details.miscSize);
+
+ for (StorageItemPreference userPref : mUsers) {
+ final long userSize = details.usersSize.get(userPref.userHandle);
+ updatePreference(userPref, userSize);
+ }
+
+ mGraph.commit();
+ }
+
+ private void updatePreference(StorageItemPreference pref, long size) {
+ pref.setSummary(Formatter.formatFileSize(getActivity(), size));
+ if (size > 0) {
+ final int order = pref.getOrder();
+ mGraph.addEntry(order, size / (float) mTotalSize, pref.color);
+ }
+ }
+
+ /**
+ * Return list of other users, excluding the current user.
+ */
+ private List<UserInfo> getUsersExcluding(UserInfo excluding) {
+ final List<UserInfo> users = mUserManager.getUsers();
+ final Iterator<UserInfo> i = users.iterator();
+ while (i.hasNext()) {
+ if (i.next().id == excluding.id) {
+ i.remove();
+ }
+ }
+ return users;
+ }
+
+ private static long totalValues(HashMap<String, Long> map, String... keys) {
+ long total = 0;
+ for (String key : keys) {
+ if (map.containsKey(key)) {
+ total += map.get(key);
+ }
+ }
+ return total;
+ }
+
+ private final StorageEventListener mStorageListener = new StorageEventListener() {
+ @Override
+ public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+ if (Objects.equals(mVolume.id, vol.id)) {
+ mVolume = vol;
+ refresh();
+ }
+ }
+ };
+
+ /**
+ * Dialog that allows editing of volume nickname.
+ */
+ public static class RenameFragment extends DialogFragment {
+ public static void show(PrivateVolumeSettings parent) {
+ if (!parent.isAdded()) return;
+
+ final RenameFragment dialog = new RenameFragment();
+ dialog.setTargetFragment(parent, 0);
+ dialog.setArguments(parent.getArguments());
+ dialog.show(parent.getFragmentManager(), TAG_RENAME);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ final StorageManager storageManager = context.getSystemService(StorageManager.class);
+
+ final String volId = getArguments().getString(EXTRA_VOLUME_ID);
+ final VolumeInfo vol = storageManager.findVolumeById(volId);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
+
+ final View view = dialogInflater.inflate(R.layout.dialog_edittext, null, false);
+ final EditText nickname = (EditText) view.findViewById(R.id.edittext);
+
+ if (!TextUtils.isEmpty(vol.nickname)) {
+ nickname.setText(vol.nickname);
+ } else {
+ nickname.setText(storageManager.getBestVolumeDescription(volId));
+ }
+
+ builder.setTitle(R.string.storage_rename_title);
+ builder.setView(view);
+
+ builder.setPositiveButton(R.string.save,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // TODO: persist the edited nickname!
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+
+ return builder.create();
+ }
+ }
+
+ /**
+ * Dialog to request user confirmation before clearing all cache data.
+ */
+ public static class ConfirmClearCacheFragment extends DialogFragment {
+ public static void show(PrivateVolumeSettings parent) {
+ if (!parent.isAdded()) return;
+
+ final ConfirmClearCacheFragment dialog = new ConfirmClearCacheFragment();
+ dialog.setTargetFragment(parent, 0);
+ dialog.show(parent.getFragmentManager(), TAG_CONFIRM_CLEAR_CACHE);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.memory_clear_cache_title);
+ builder.setMessage(getString(R.string.memory_clear_cache_message));
+
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final PrivateVolumeSettings target = (PrivateVolumeSettings) getTargetFragment();
+ final PackageManager pm = context.getPackageManager();
+ final List<PackageInfo> infos = pm.getInstalledPackages(0);
+ final ClearCacheObserver observer = new ClearCacheObserver(
+ target, infos.size());
+ for (PackageInfo info : infos) {
+ pm.deleteApplicationCacheFiles(info.packageName, observer);
+ }
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+
+ return builder.create();
+ }
+ }
+
+ private static class ClearCacheObserver extends IPackageDataObserver.Stub {
+ private final PrivateVolumeSettings mTarget;
+ private int mRemaining;
+
+ public ClearCacheObserver(PrivateVolumeSettings target, int remaining) {
+ mTarget = target;
+ mRemaining = remaining;
+ }
+
+ @Override
+ public void onRemoveCompleted(final String packageName, final boolean succeeded) {
+ synchronized (this) {
+ if (--mRemaining == 0) {
+ mTarget.refresh();
+ }
+ }
+ }
+ }
+
+ public static class PreferenceHeader extends Preference {
+ public PreferenceHeader(Context context, int titleRes) {
+ super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
+ setTitle(titleRes);
+ }
+
+ public PreferenceHeader(Context context, CharSequence title) {
+ super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
+ setTitle(title);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return false;
+ }
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/PublicVolumeSettings.java b/src/com/android/settings/deviceinfo/PublicVolumeSettings.java
new file mode 100644
index 0000000..6edfc92
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/PublicVolumeSettings.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2015 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 static com.android.settings.deviceinfo.StorageSettings.EXTRA_VOLUME_ID;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.provider.DocumentsContract;
+import android.text.format.Formatter;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.Preconditions;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.deviceinfo.StorageSettings.FormatTask;
+import com.android.settings.deviceinfo.StorageSettings.MountTask;
+import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
+
+import java.io.File;
+import java.util.Objects;
+
+/**
+ * Panel showing summary and actions for a {@link VolumeInfo#TYPE_PUBLIC}
+ * storage volume.
+ */
+public class PublicVolumeSettings extends SettingsPreferenceFragment {
+ // TODO: disable unmount when providing over MTP/PTP
+
+ private static final String PREF_FORMAT_INTERNAL = "debug.format_internal";
+
+ private StorageManager mStorageManager;
+
+ private VolumeInfo mVolume;
+
+ private int mNextOrder = 0;
+
+ private UsageBarPreference mGraph;
+ private StorageItemPreference mTotal;
+ private StorageItemPreference mAvailable;
+
+ private Preference mMount;
+ private Preference mUnmount;
+ private Preference mFormat;
+ private Preference mFormatInternal;
+
+ private long mTotalSize;
+ private long mAvailSize;
+
+ @Override
+ protected int getMetricsCategory() {
+ return MetricsLogger.DEVICEINFO_STORAGE;
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ final Context context = getActivity();
+
+ mStorageManager = context.getSystemService(StorageManager.class);
+
+ if (DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS.equals(
+ getActivity().getIntent().getAction())) {
+ final Uri rootUri = getActivity().getIntent().getData();
+ final String fsUuid = DocumentsContract.getRootId(rootUri);
+ mVolume = mStorageManager.findVolumeByUuid(fsUuid);
+ } else {
+ final String volId = getArguments().getString(EXTRA_VOLUME_ID);
+ mVolume = mStorageManager.findVolumeById(volId);
+ }
+
+ Preconditions.checkNotNull(mVolume);
+ Preconditions.checkState(mVolume.type == VolumeInfo.TYPE_PUBLIC);
+
+ addPreferencesFromResource(R.xml.device_info_storage_volume);
+
+ mGraph = buildGraph();
+ mTotal = buildItem(R.string.memory_size, 0);
+ mAvailable = buildItem(R.string.memory_available, R.color.memory_avail);
+
+ mMount = buildAction(R.string.storage_menu_mount);
+ mUnmount = buildAction(R.string.storage_menu_unmount);
+ mFormat = buildAction(R.string.storage_menu_format);
+ mFormatInternal = buildAction(R.string.storage_menu_format_internal);
+ }
+
+ public void refresh() {
+ getActivity().setTitle(mStorageManager.getBestVolumeDescription(mVolume.id));
+
+ final Context context = getActivity();
+ final PreferenceScreen screen = getPreferenceScreen();
+
+ screen.removeAll();
+
+ if (mVolume.state == VolumeInfo.STATE_MOUNTED) {
+ screen.addPreference(mGraph);
+ screen.addPreference(mTotal);
+ screen.addPreference(mAvailable);
+ }
+
+ if (mVolume.state == VolumeInfo.STATE_UNMOUNTED) {
+ screen.addPreference(mMount);
+ }
+ if (mVolume.state == VolumeInfo.STATE_MOUNTED) {
+ screen.addPreference(mUnmount);
+ }
+ screen.addPreference(mFormat);
+ if (SystemProperties.getBoolean(PREF_FORMAT_INTERNAL, false)) {
+ screen.addPreference(mFormatInternal);
+ }
+
+ final File file = new File(mVolume.path);
+ mTotalSize = file.getTotalSpace();
+ mAvailSize = file.getFreeSpace();
+
+ mTotal.setSummary(Formatter.formatFileSize(context, mTotalSize));
+ mAvailable.setSummary(Formatter.formatFileSize(context, mAvailSize));
+
+ mGraph.clear();
+ mGraph.addEntry(0, (mTotalSize - mAvailSize) / (float) mTotalSize,
+ android.graphics.Color.GRAY);
+ mGraph.commit();
+ }
+
+ private UsageBarPreference buildGraph() {
+ final UsageBarPreference pref = new UsageBarPreference(getActivity());
+ pref.setOrder(mNextOrder++);
+ return pref;
+ }
+
+ private StorageItemPreference buildItem(int titleRes, int colorRes) {
+ final StorageItemPreference pref = new StorageItemPreference(getActivity(), titleRes,
+ colorRes);
+ pref.setOrder(mNextOrder++);
+ return pref;
+ }
+
+ private Preference buildAction(int titleRes) {
+ final Preference pref = new Preference(getActivity());
+ pref.setTitle(titleRes);
+ pref.setOrder(mNextOrder++);
+ return pref;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mStorageManager.registerListener(mStorageListener);
+ refresh();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mStorageManager.unregisterListener(mStorageListener);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference pref) {
+ final Context context = getActivity();
+ if (pref == mMount) {
+ new MountTask(context, mVolume.id).execute();
+ } else if (pref == mUnmount) {
+ new UnmountTask(context, mVolume.id).execute();
+ } else if (pref == mFormat) {
+ new FormatTask(context, mVolume.id).execute();
+ } else if (pref == mFormatInternal) {
+ // TODO: implement this
+ }
+
+ return super.onPreferenceTreeClick(preferenceScreen, pref);
+ }
+
+ private final StorageEventListener mStorageListener = new StorageEventListener() {
+ @Override
+ public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+ if (Objects.equals(mVolume.id, vol.id)) {
+ mVolume = vol;
+ refresh();
+ }
+ }
+ };
+}
diff --git a/src/com/android/settings/deviceinfo/StorageItemPreference.java b/src/com/android/settings/deviceinfo/StorageItemPreference.java
index 87e827e..8d48cf0 100644
--- a/src/com/android/settings/deviceinfo/StorageItemPreference.java
+++ b/src/com/android/settings/deviceinfo/StorageItemPreference.java
@@ -62,4 +62,8 @@ public class StorageItemPreference extends Preference {
shape.getPaint().setColor(color);
return shape;
}
+
+ public void setLoading() {
+ setSummary(R.string.memory_calculating_size);
+ }
}
diff --git a/src/com/android/settings/deviceinfo/StorageMeasurement.java b/src/com/android/settings/deviceinfo/StorageMeasurement.java
index 34ef62b..db91fdb 100644
--- a/src/com/android/settings/deviceinfo/StorageMeasurement.java
+++ b/src/com/android/settings/deviceinfo/StorageMeasurement.java
@@ -27,7 +27,6 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.content.pm.UserInfo;
import android.os.Environment;
-import android.os.Environment.UserEnvironment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -36,23 +35,22 @@ import android.os.Message;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
import android.util.Log;
import android.util.SparseLongArray;
import com.android.internal.app.IMediaContainerService;
-import com.google.android.collect.Maps;
+import com.android.internal.util.ArrayUtils;
import com.google.android.collect.Sets;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
-import javax.annotation.concurrent.GuardedBy;
-
/**
* Utility for measuring the disk usage of internal storage or a physical
* {@link StorageVolume}. Connects with a remote {@link IMediaContainerService}
@@ -77,28 +75,7 @@ public class StorageMeasurement {
Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS,
Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID);
- @GuardedBy("sInstances")
- private static HashMap<StorageVolume, StorageMeasurement> sInstances = Maps.newHashMap();
-
- /**
- * Obtain shared instance of {@link StorageMeasurement} for given physical
- * {@link StorageVolume}, or internal storage if {@code null}.
- */
- public static StorageMeasurement getInstance(Context context, StorageVolume volume) {
- synchronized (sInstances) {
- StorageMeasurement value = sInstances.get(volume);
- if (value == null) {
- value = new StorageMeasurement(context.getApplicationContext(), volume);
- sInstances.put(volume, value);
- }
- return value;
- }
- }
-
public static class MeasurementDetails {
- public long totalSize;
- public long availSize;
-
/**
* Total apps disk usage.
* <p>
@@ -128,7 +105,7 @@ public class StorageMeasurement {
* When measuring a physical {@link StorageVolume}, this reflects media
* on that volume.
*/
- public HashMap<String, Long> mediaSize = Maps.newHashMap();
+ public HashMap<String, Long> mediaSize = new HashMap<>();
/**
* Misc external disk usage for the current user, unaccounted in
@@ -144,34 +121,31 @@ public class StorageMeasurement {
}
public interface MeasurementReceiver {
- public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize);
- public void updateDetails(StorageMeasurement meas, MeasurementDetails details);
+ public void onDetailsChanged(MeasurementDetails details);
}
- private volatile WeakReference<MeasurementReceiver> mReceiver;
-
- /** Physical volume being measured, or {@code null} for internal. */
- private final StorageVolume mVolume;
+ private WeakReference<MeasurementReceiver> mReceiver;
- private final boolean mIsInternal;
- private final boolean mIsPrimary;
+ private final Context mContext;
- private final MeasurementHandler mHandler;
+ private final VolumeInfo mVolume;
+ private final VolumeInfo mSharedVolume;
- private long mTotalSize;
- private long mAvailSize;
+ private final MainHandler mMainHandler;
+ private final MeasurementHandler mMeasurementHandler;
- List<FileInfo> mFileInfoForMisc;
+ public StorageMeasurement(Context context, VolumeInfo volume, VolumeInfo sharedVolume) {
+ mContext = context.getApplicationContext();
- private StorageMeasurement(Context context, StorageVolume volume) {
mVolume = volume;
- mIsInternal = volume == null;
- mIsPrimary = volume != null ? volume.isPrimary() : false;
+ mSharedVolume = sharedVolume;
// Start the thread that will measure the disk usage.
final HandlerThread handlerThread = new HandlerThread("MemoryMeasurement");
handlerThread.start();
- mHandler = new MeasurementHandler(context, handlerThread.getLooper());
+
+ mMainHandler = new MainHandler();
+ mMeasurementHandler = new MeasurementHandler(handlerThread.getLooper());
}
public void setReceiver(MeasurementReceiver receiver) {
@@ -180,52 +154,38 @@ public class StorageMeasurement {
}
}
+ public void forceMeasure() {
+ invalidate();
+ measure();
+ }
+
public void measure() {
- if (!mHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
- mHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
+ if (!mMeasurementHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
}
}
- public void cleanUp() {
+ public void onDestroy() {
mReceiver = null;
- mHandler.removeMessages(MeasurementHandler.MSG_MEASURE);
- mHandler.sendEmptyMessage(MeasurementHandler.MSG_DISCONNECT);
- }
-
- public void invalidate() {
- mHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE);
+ mMeasurementHandler.removeMessages(MeasurementHandler.MSG_MEASURE);
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_DISCONNECT);
}
- private void sendInternalApproximateUpdate() {
- MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
- if (receiver == null) {
- return;
- }
- receiver.updateApproximate(this, mTotalSize, mAvailSize);
- }
-
- private void sendExactUpdate(MeasurementDetails details) {
- MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
- if (receiver == null) {
- if (LOGV) {
- Log.i(TAG, "measurements dropped because receiver is null! wasted effort");
- }
- return;
- }
- receiver.updateDetails(this, details);
+ private void invalidate() {
+ mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE);
}
private static class StatsObserver extends IPackageStatsObserver.Stub {
- private final boolean mIsInternal;
+ private final boolean mIsPrivate;
private final MeasurementDetails mDetails;
private final int mCurrentUser;
private final Message mFinished;
private int mRemaining;
- public StatsObserver(boolean isInternal, MeasurementDetails details, int currentUser,
+ public StatsObserver(boolean isPrivate, MeasurementDetails details, int currentUser,
Message finished, int remaining) {
- mIsInternal = isInternal;
+ mIsPrivate = isPrivate;
mDetails = details;
mCurrentUser = currentUser;
mFinished = finished;
@@ -245,7 +205,7 @@ public class StorageMeasurement {
}
private void addStatsLocked(PackageStats stats) {
- if (mIsInternal) {
+ if (mIsPrivate) {
long codeSize = stats.codeSize;
long dataSize = stats.dataSize;
long cacheSize = stats.cacheSize;
@@ -279,6 +239,17 @@ public class StorageMeasurement {
}
}
+ private class MainHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ final MeasurementDetails details = (MeasurementDetails) msg.obj;
+ final MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
+ if (receiver != null) {
+ receiver.onDetailsChanged(details);
+ }
+ }
+ }
+
private class MeasurementHandler extends Handler {
public static final int MSG_MEASURE = 1;
public static final int MSG_CONNECTED = 2;
@@ -294,8 +265,6 @@ public class StorageMeasurement {
private MeasurementDetails mCached;
- private final WeakReference<Context> mContext;
-
private final ServiceConnection mDefContainerConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -313,9 +282,8 @@ public class StorageMeasurement {
}
};
- public MeasurementHandler(Context context, Looper looper) {
+ public MeasurementHandler(Looper looper) {
super(looper);
- mContext = new WeakReference<Context>(context);
}
@Override
@@ -323,50 +291,39 @@ public class StorageMeasurement {
switch (msg.what) {
case MSG_MEASURE: {
if (mCached != null) {
- sendExactUpdate(mCached);
+ mMainHandler.obtainMessage(0, mCached).sendToTarget();
break;
}
- final Context context = (mContext != null) ? mContext.get() : null;
- if (context == null) {
- return;
- }
-
synchronized (mLock) {
if (mBound) {
removeMessages(MSG_DISCONNECT);
sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
} else {
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
- context.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
- UserHandle.OWNER);
+ mContext.bindServiceAsUser(service, mDefContainerConn,
+ Context.BIND_AUTO_CREATE, UserHandle.OWNER);
}
}
break;
}
case MSG_CONNECTED: {
- IMediaContainerService imcs = (IMediaContainerService) msg.obj;
- measureApproximateStorage(imcs);
+ final 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);
+ mContext.unbindService(mDefContainerConn);
}
}
break;
}
case MSG_COMPLETED: {
mCached = (MeasurementDetails) msg.obj;
- sendExactUpdate(mCached);
+ mMainHandler.obtainMessage(0, mCached).sendToTarget();
break;
}
case MSG_INVALIDATE: {
@@ -375,87 +332,74 @@ public class StorageMeasurement {
}
}
}
+ }
- private void measureApproximateStorage(IMediaContainerService imcs) {
- final String path = mVolume != null ? mVolume.getPath()
- : Environment.getDataDirectory().getPath();
- try {
- final long[] stats = imcs.getFileSystemStats(path);
- mTotalSize = stats[0];
- mAvailSize = stats[1];
- } catch (Exception e) {
- Log.w(TAG, "Problem in container service", e);
- }
-
- sendInternalApproximateUpdate();
- }
-
- private void measureExactStorage(IMediaContainerService imcs) {
- final Context context = mContext != null ? mContext.get() : null;
- if (context == null) {
- return;
- }
-
- final MeasurementDetails details = new MeasurementDetails();
- final Message finished = obtainMessage(MSG_COMPLETED, details);
+ private void measureExactStorage(IMediaContainerService imcs) {
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
+ final PackageManager packageManager = mContext.getPackageManager();
- details.totalSize = mTotalSize;
- details.availSize = mAvailSize;
+ final List<UserInfo> users = userManager.getUsers();
+ final int currentUser = ActivityManager.getCurrentUser();
- final UserManager userManager = (UserManager) context.getSystemService(
- Context.USER_SERVICE);
- final List<UserInfo> users = userManager.getUsers();
+ final MeasurementDetails details = new MeasurementDetails();
+ final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED,
+ details);
- final int currentUser = ActivityManager.getCurrentUser();
- final UserEnvironment currentEnv = new UserEnvironment(currentUser);
+ if (mSharedVolume != null && mSharedVolume.state == VolumeInfo.STATE_MOUNTED) {
+ final File basePath = mSharedVolume.getPathForUser(currentUser);
// Measure media types for emulated storage, or for primary physical
// external volume
- final boolean measureMedia = (mIsInternal && Environment.isExternalStorageEmulated())
- || mIsPrimary;
- if (measureMedia) {
- for (String type : sMeasureMediaTypes) {
- final File path = currentEnv.getExternalStoragePublicDirectory(type);
- final long size = getDirectorySize(imcs, path);
- details.mediaSize.put(type, size);
- }
+ for (String type : sMeasureMediaTypes) {
+ final File path = new File(basePath, type);
+ final long size = getDirectorySize(imcs, path);
+ details.mediaSize.put(type, size);
}
// Measure misc files not counted under media
- if (measureMedia) {
- final File path = mIsInternal ? currentEnv.getExternalStorageDirectory()
- : mVolume.getPathFile();
- details.miscSize = measureMisc(imcs, path);
- }
+ details.miscSize = measureMisc(imcs, basePath);
- // Measure total emulated storage of all users; internal apps data
- // will be spliced in later
- for (UserInfo user : users) {
- final UserEnvironment userEnv = new UserEnvironment(user.id);
- final long size = getDirectorySize(imcs, userEnv.getExternalStorageDirectory());
- addValue(details.usersSize, user.id, size);
+ if (mSharedVolume.type == VolumeInfo.TYPE_EMULATED) {
+ // Measure total emulated storage of all users; internal apps data
+ // will be spliced in later
+ for (UserInfo user : users) {
+ final File userPath = mSharedVolume.getPathForUser(user.id);
+ final long size = getDirectorySize(imcs, userPath);
+ addValue(details.usersSize, user.id, size);
+ }
}
+ }
- // Measure all apps for all users
- final PackageManager pm = context.getPackageManager();
- if (mIsInternal || mIsPrimary) {
- final List<ApplicationInfo> apps = pm.getInstalledApplications(
- PackageManager.GET_UNINSTALLED_PACKAGES
- | PackageManager.GET_DISABLED_COMPONENTS);
-
- final int count = users.size() * apps.size();
- final StatsObserver observer = new StatsObserver(
- mIsInternal, details, currentUser, finished, count);
+ // Measure all apps hosted on this volume for all users
+ if (mVolume.type == VolumeInfo.TYPE_PRIVATE) {
+ final List<ApplicationInfo> apps = packageManager.getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS);
- for (UserInfo user : users) {
- for (ApplicationInfo app : apps) {
- pm.getPackageSizeInfo(app.packageName, user.id, observer);
- }
+ final List<ApplicationInfo> volumeApps = new ArrayList<>();
+ for (ApplicationInfo app : apps) {
+ if (Objects.equals(app.volumeUuid, mVolume.fsUuid)) {
+ volumeApps.add(app);
}
+ }
- } else {
+ final int count = users.size() * volumeApps.size();
+ if (count == 0) {
finished.sendToTarget();
+ return;
+ }
+
+ final StatsObserver observer = new StatsObserver(
+ true, details, currentUser, finished, count);
+ for (UserInfo user : users) {
+ for (ApplicationInfo app : volumeApps) {
+ packageManager.getPackageSizeInfo(app.packageName, user.id, observer);
+ }
}
+
+ } else {
+ finished.sendToTarget();
+ return;
}
}
@@ -471,64 +415,26 @@ public class StorageMeasurement {
}
private long measureMisc(IMediaContainerService imcs, File dir) {
- mFileInfoForMisc = new ArrayList<FileInfo>();
-
final File[] files = dir.listFiles();
- if (files == null) return 0;
+ if (ArrayUtils.isEmpty(files)) return 0;
// Get sizes of all top level nodes except the ones already computed
- long counter = 0;
long miscSize = 0;
-
for (File file : files) {
- final String path = file.getAbsolutePath();
final String name = file.getName();
if (sMeasureMediaTypes.contains(name)) {
continue;
}
if (file.isFile()) {
- final long fileSize = file.length();
- mFileInfoForMisc.add(new FileInfo(path, fileSize, counter++));
- miscSize += fileSize;
+ miscSize += file.length();
} else if (file.isDirectory()) {
- final long dirSize = getDirectorySize(imcs, file);
- mFileInfoForMisc.add(new FileInfo(path, dirSize, counter++));
- miscSize += dirSize;
- } else {
- // Non directory, non file: not listed
+ miscSize += getDirectorySize(imcs, file);
}
}
-
- // sort the list of FileInfo objects collected above in descending order of their sizes
- Collections.sort(mFileInfoForMisc);
-
return miscSize;
}
- static class FileInfo implements Comparable<FileInfo> {
- 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;
- }
- }
-
private static void addValue(SparseLongArray array, int key, long value) {
array.put(key, array.get(key) + value);
}
diff --git a/src/com/android/settings/deviceinfo/StorageSettings.java b/src/com/android/settings/deviceinfo/StorageSettings.java
new file mode 100644
index 0000000..001f00d
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/StorageSettings.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2015 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.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.preference.Preference;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceScreen;
+import android.provider.DocumentsContract;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchIndexableRaw;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Panel showing both internal storage (both built-in storage and private
+ * volumes) and removable storage (public volumes).
+ */
+public class StorageSettings extends SettingsPreferenceFragment implements Indexable {
+ static final String TAG = "StorageSettings";
+
+ // TODO: badging to indicate devices running low on storage
+ // TODO: show currently ejected private volumes
+
+ public static final String EXTRA_VOLUME_ID = "volume_id";
+
+ private static final String DOCUMENT_AUTHORITY = "com.android.externalstorage.documents";
+ private static final String DOCUMENT_ROOT_PRIMARY_EMULATED = "primary";
+
+ /**
+ * Build an intent to browse the contents of given {@link VolumeInfo}.
+ */
+ public static Intent buildBrowseIntent(VolumeInfo vol) {
+ final Uri uri;
+ if (vol.type == VolumeInfo.TYPE_PUBLIC) {
+ uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, vol.fsUuid);
+ } else if (VolumeInfo.ID_EMULATED_INTERNAL.equals(vol.id)) {
+ uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
+ DOCUMENT_ROOT_PRIMARY_EMULATED);
+ } else if (vol.type == VolumeInfo.TYPE_EMULATED) {
+ // TODO: build intent once supported
+ uri = null;
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setData(uri);
+ return intent;
+ }
+
+ private UserManager mUserManager;
+ private StorageManager mStorageManager;
+
+ private PreferenceCategory mInternalCategory;
+ private PreferenceCategory mExternalCategory;
+
+ @Override
+ protected int getMetricsCategory() {
+ return MetricsLogger.DEVICEINFO_STORAGE;
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ final Context context = getActivity();
+
+ mUserManager = context.getSystemService(UserManager.class);
+
+ mStorageManager = context.getSystemService(StorageManager.class);
+ mStorageManager.registerListener(mStorageListener);
+
+ addPreferencesFromResource(R.xml.device_info_storage);
+
+ mInternalCategory = (PreferenceCategory) findPreference("storage_internal");
+ mExternalCategory = (PreferenceCategory) findPreference("storage_external");
+
+ // TODO: if only one volume visible, shortcut into it
+
+ setHasOptionsMenu(true);
+ }
+
+ private static final Comparator<VolumeInfo> sVolumeComparator = new Comparator<VolumeInfo>() {
+ @Override
+ public int compare(VolumeInfo lhs, VolumeInfo rhs) {
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(lhs.id)) {
+ return -1;
+ } else if (lhs.getDescription() == null) {
+ return 1;
+ } else {
+ return lhs.getDescription().compareTo(rhs.getDescription());
+ }
+ }
+ };
+
+ private final StorageEventListener mStorageListener = new StorageEventListener() {
+ @Override
+ public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+ if (isInteresting(vol)) {
+ refresh();
+ }
+ }
+ };
+
+ private static boolean isInteresting(VolumeInfo vol) {
+ return vol.type == VolumeInfo.TYPE_PRIVATE || vol.type == VolumeInfo.TYPE_PUBLIC;
+ }
+
+ private void refresh() {
+ final Context context = getActivity();
+
+ getPreferenceScreen().removeAll();
+ mInternalCategory.removeAll();
+ mExternalCategory.removeAll();
+
+ final List<VolumeInfo> volumes = mStorageManager.getVolumes();
+ Collections.sort(volumes, sVolumeComparator);
+
+ for (VolumeInfo vol : volumes) {
+ if (vol.type == VolumeInfo.TYPE_PRIVATE) {
+ mInternalCategory.addPreference(new StorageVolumePreference(context, vol));
+ } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
+ mExternalCategory.addPreference(new StorageVolumePreference(context, vol));
+ }
+ }
+
+ if (mInternalCategory.getPreferenceCount() > 0) {
+ getPreferenceScreen().addPreference(mInternalCategory);
+ }
+ if (mExternalCategory.getPreferenceCount() > 0) {
+ getPreferenceScreen().addPreference(mExternalCategory);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mStorageManager.registerListener(mStorageListener);
+ refresh();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mStorageManager.unregisterListener(mStorageListener);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.storage, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ final MenuItem usb = menu.findItem(R.id.storage_usb);
+
+ usb.setVisible(!mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.storage_usb:
+ startFragment(this, UsbSettings.class.getCanonicalName(),
+ R.string.storage_title_usb, 0, null);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference pref) {
+ final String volId = pref.getKey();
+ final VolumeInfo vol = mStorageManager.findVolumeById(volId);
+ if (vol == null) {
+ return false;
+
+ } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
+ final Bundle args = new Bundle();
+ args.putString(EXTRA_VOLUME_ID, volId);
+ startFragment(this, PrivateVolumeSettings.class.getCanonicalName(),
+ -1, 0, args);
+ return true;
+
+ } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
+ if (vol.state == VolumeInfo.STATE_MOUNTED) {
+ final Intent intent = buildBrowseIntent(vol);
+ startActivity(intent);
+ return true;
+ } else {
+ final Bundle args = new Bundle();
+ args.putString(EXTRA_VOLUME_ID, volId);
+ startFragment(this, PublicVolumeSettings.class.getCanonicalName(),
+ -1, 0, args);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static class MountTask extends AsyncTask<Void, Void, Exception> {
+ private final Context mContext;
+ private final StorageManager mStorageManager;
+ private final String mVolumeId;
+ private final String mDescription;
+
+ public MountTask(Context context, String volumeId) {
+ mContext = context.getApplicationContext();
+ mStorageManager = mContext.getSystemService(StorageManager.class);
+ mVolumeId = volumeId;
+ mDescription = mStorageManager.getBestVolumeDescription(mVolumeId);
+ }
+
+ @Override
+ protected Exception doInBackground(Void... params) {
+ try {
+ mStorageManager.mount(mVolumeId);
+ return null;
+ } catch (Exception e) {
+ return e;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Exception e) {
+ if (e == null) {
+ Toast.makeText(mContext, mContext.getString(R.string.storage_mount_success,
+ mDescription), Toast.LENGTH_SHORT).show();
+ } else {
+ Log.e(TAG, "Failed to mount " + mVolumeId, e);
+ Toast.makeText(mContext, mContext.getString(R.string.storage_mount_failure,
+ mDescription), Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ public static class UnmountTask extends AsyncTask<Void, Void, Exception> {
+ private final Context mContext;
+ private final StorageManager mStorageManager;
+ private final String mVolumeId;
+ private final String mDescription;
+
+ public UnmountTask(Context context, String volumeId) {
+ mContext = context.getApplicationContext();
+ mStorageManager = mContext.getSystemService(StorageManager.class);
+ mVolumeId = volumeId;
+ mDescription = mStorageManager.getBestVolumeDescription(mVolumeId);
+ }
+
+ @Override
+ protected Exception doInBackground(Void... params) {
+ try {
+ mStorageManager.unmount(mVolumeId);
+ return null;
+ } catch (Exception e) {
+ return e;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Exception e) {
+ if (e == null) {
+ Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_success,
+ mDescription), Toast.LENGTH_SHORT).show();
+ } else {
+ Log.e(TAG, "Failed to unmount " + mVolumeId, e);
+ Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_failure,
+ mDescription), Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ public static class FormatTask extends AsyncTask<Void, Void, Exception> {
+ private final Context mContext;
+ private final StorageManager mStorageManager;
+ private final String mVolumeId;
+ private final String mDescription;
+
+ public FormatTask(Context context, String volumeId) {
+ mContext = context.getApplicationContext();
+ mStorageManager = mContext.getSystemService(StorageManager.class);
+ mVolumeId = volumeId;
+ mDescription = mStorageManager.getBestVolumeDescription(mVolumeId);
+ }
+
+ @Override
+ protected Exception doInBackground(Void... params) {
+ try {
+ mStorageManager.format(mVolumeId);
+ mStorageManager.mount(mVolumeId);
+ return null;
+ } catch (Exception e) {
+ return e;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Exception e) {
+ if (e == null) {
+ Toast.makeText(mContext, mContext.getString(R.string.storage_format_success,
+ mDescription), Toast.LENGTH_SHORT).show();
+ } else {
+ Log.e(TAG, "Failed to format " + mVolumeId, e);
+ Toast.makeText(mContext, mContext.getString(R.string.storage_format_failure,
+ mDescription), Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ /**
+ * Enable indexing of searchable data
+ */
+ public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {
+ @Override
+ public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
+ final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
+
+ SearchIndexableRaw data = new SearchIndexableRaw(context);
+ data.title = context.getString(R.string.storage_settings);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+
+ data = new SearchIndexableRaw(context);
+ data.title = context.getString(R.string.internal_storage);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+
+ data = new SearchIndexableRaw(context);
+ final StorageManager storage = context.getSystemService(StorageManager.class);
+ final List<VolumeInfo> vols = storage.getVolumes();
+ for (VolumeInfo vol : vols) {
+ if (isInteresting(vol)) {
+ data.title = storage.getBestVolumeDescription(vol.id);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+ }
+ }
+
+ data = new SearchIndexableRaw(context);
+ data.title = context.getString(R.string.memory_size);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+
+ data = new SearchIndexableRaw(context);
+ data.title = context.getString(R.string.memory_available);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+
+ data = new SearchIndexableRaw(context);
+ data.title = context.getString(R.string.memory_apps_usage);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+
+ data = new SearchIndexableRaw(context);
+ data.title = context.getString(R.string.memory_dcim_usage);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+
+ data = new SearchIndexableRaw(context);
+ data.title = context.getString(R.string.memory_music_usage);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+
+ data = new SearchIndexableRaw(context);
+ data.title = context.getString(R.string.memory_downloads_usage);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+
+ data = new SearchIndexableRaw(context);
+ data.title = context.getString(R.string.memory_media_cache_usage);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+
+ data = new SearchIndexableRaw(context);
+ data.title = context.getString(R.string.memory_media_misc_usage);
+ data.screenTitle = context.getString(R.string.storage_settings);
+ result.add(data);
+
+ return result;
+ }
+ };
+}
diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreference.java b/src/com/android/settings/deviceinfo/StorageVolumePreference.java
new file mode 100644
index 0000000..fbe34f6
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/StorageVolumePreference.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 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.Context;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.preference.Preference;
+import android.text.format.Formatter;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
+
+import java.io.File;
+
+/**
+ * Preference line representing a single {@link VolumeInfo}, possibly including
+ * quick actions like unmounting.
+ */
+public class StorageVolumePreference extends Preference {
+ private final StorageManager mStorageManager;
+ private final VolumeInfo mVolume;
+
+ public StorageVolumePreference(Context context, VolumeInfo volume) {
+ super(context);
+
+ mStorageManager = context.getSystemService(StorageManager.class);
+ mVolume = volume;
+
+ setKey(volume.id);
+ setTitle(mStorageManager.getBestVolumeDescription(volume.id));
+
+ switch (volume.state) {
+ case VolumeInfo.STATE_MOUNTED:
+ // TODO: move statfs() to background thread
+ final File path = new File(volume.path);
+ final String free = Formatter.formatFileSize(context, path.getFreeSpace());
+ final String total = Formatter.formatFileSize(context, path.getTotalSpace());
+ setSummary(context.getString(R.string.storage_volume_summary, free, total));
+ break;
+ }
+
+ // TODO: better icons
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(volume.id)) {
+ setIcon(context.getDrawable(R.drawable.ic_settings_storage));
+ } else {
+ setIcon(context.getDrawable(R.drawable.ic_sim_sd));
+ }
+
+ if (volume.type == VolumeInfo.TYPE_PUBLIC && volume.state == VolumeInfo.STATE_MOUNTED) {
+ setWidgetLayoutResource(R.layout.preference_storage_action);
+ }
+ }
+
+ @Override
+ protected void onBindView(View view) {
+ final TextView unmount = (TextView) view.findViewById(R.id.unmount);
+ if (unmount != null) {
+ unmount.setText("\u23CF");
+ unmount.setOnClickListener(mUnmountListener);
+ }
+
+ super.onBindView(view);
+ }
+
+ private final View.OnClickListener mUnmountListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ new UnmountTask(getContext(), mVolume.id).execute();
+ }
+ };
+}
diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
deleted file mode 100644
index a98f8d9..0000000
--- a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * 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.ActivityManagerNative;
-import android.app.ActivityThread;
-import android.app.DownloadManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.IPackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.hardware.usb.UsbManager;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.UserManager;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
-import android.preference.Preference;
-import android.preference.PreferenceCategory;
-import android.provider.MediaStore;
-import android.text.format.Formatter;
-
-import com.android.settings.R;
-import com.android.settings.Settings;
-import com.android.settings.deviceinfo.StorageMeasurement.MeasurementDetails;
-import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
-import com.google.android.collect.Lists;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-public class StorageVolumePreferenceCategory extends PreferenceCategory {
- public static final String KEY_CACHE = "cache";
-
- private static final int ORDER_USAGE_BAR = -2;
- private static final int ORDER_STORAGE_LOW = -1;
-
- /** Physical volume being measured, or {@code null} for internal. */
- private final StorageVolume mVolume;
- private final StorageMeasurement mMeasure;
-
- private final Resources mResources;
- private final StorageManager mStorageManager;
- private final UserManager mUserManager;
-
- private UsageBarPreference mUsageBarPreference;
- private Preference mMountTogglePreference;
- private Preference mFormatPreference;
- private Preference mStorageLow;
-
- private StorageItemPreference mItemTotal;
- private StorageItemPreference mItemAvailable;
- private StorageItemPreference mItemApps;
- private StorageItemPreference mItemDcim;
- private StorageItemPreference mItemMusic;
- private StorageItemPreference mItemDownloads;
- private StorageItemPreference mItemCache;
- private StorageItemPreference mItemMisc;
- private List<StorageItemPreference> mItemUsers = Lists.newArrayList();
-
- private boolean mUsbConnected;
- private String mUsbFunction;
-
- private long mTotalSize;
-
- private static final int MSG_UI_UPDATE_APPROXIMATE = 1;
- private static final int MSG_UI_UPDATE_DETAILS = 2;
-
- private Handler mUpdateHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UI_UPDATE_APPROXIMATE: {
- final long[] size = (long[]) msg.obj;
- updateApproximate(size[0], size[1]);
- break;
- }
- case MSG_UI_UPDATE_DETAILS: {
- final MeasurementDetails details = (MeasurementDetails) msg.obj;
- updateDetails(details);
- break;
- }
- }
- }
- };
-
- /**
- * Build category to summarize internal storage, including any emulated
- * {@link StorageVolume}.
- */
- public static StorageVolumePreferenceCategory buildForInternal(Context context) {
- return new StorageVolumePreferenceCategory(context, null);
- }
-
- /**
- * Build category to summarize specific physical {@link StorageVolume}.
- */
- public static StorageVolumePreferenceCategory buildForPhysical(
- Context context, StorageVolume volume) {
- return new StorageVolumePreferenceCategory(context, volume);
- }
-
- private StorageVolumePreferenceCategory(Context context, StorageVolume volume) {
- super(context);
-
- mVolume = volume;
- mMeasure = StorageMeasurement.getInstance(context, volume);
-
- mResources = context.getResources();
- mStorageManager = StorageManager.from(context);
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-
- setTitle(volume != null ? volume.getDescription(context)
- : context.getText(R.string.internal_storage));
- }
-
- private StorageItemPreference buildItem(int titleRes, int colorRes) {
- return new StorageItemPreference(getContext(), titleRes, colorRes);
- }
-
- public void init() {
- final Context context = getContext();
-
- removeAll();
-
- final UserInfo currentUser;
- try {
- currentUser = ActivityManagerNative.getDefault().getCurrentUser();
- } catch (RemoteException e) {
- throw new RuntimeException("Failed to get current user");
- }
-
- final List<UserInfo> otherUsers = getUsersExcluding(currentUser);
- final boolean showUsers = mVolume == null && otherUsers.size() > 0;
-
- mUsageBarPreference = new UsageBarPreference(context);
- mUsageBarPreference.setOrder(ORDER_USAGE_BAR);
- addPreference(mUsageBarPreference);
-
- mItemTotal = buildItem(R.string.memory_size, 0);
- mItemAvailable = buildItem(R.string.memory_available, R.color.memory_avail);
- addPreference(mItemTotal);
- addPreference(mItemAvailable);
-
- mItemApps = buildItem(R.string.memory_apps_usage, R.color.memory_apps_usage);
- mItemDcim = buildItem(R.string.memory_dcim_usage, R.color.memory_dcim);
- mItemMusic = buildItem(R.string.memory_music_usage, R.color.memory_music);
- mItemDownloads = buildItem(R.string.memory_downloads_usage, R.color.memory_downloads);
- mItemCache = buildItem(R.string.memory_media_cache_usage, R.color.memory_cache);
- mItemMisc = buildItem(R.string.memory_media_misc_usage, R.color.memory_misc);
-
- mItemCache.setKey(KEY_CACHE);
-
- final boolean showDetails = mVolume == null || mVolume.isPrimary();
- if (showDetails) {
- if (showUsers) {
- addPreference(new PreferenceHeader(context, currentUser.name));
- }
-
- addPreference(mItemApps);
- addPreference(mItemDcim);
- addPreference(mItemMusic);
- addPreference(mItemDownloads);
- addPreference(mItemCache);
- addPreference(mItemMisc);
-
- if (showUsers) {
- addPreference(new PreferenceHeader(context, R.string.storage_other_users));
-
- int count = 0;
- for (UserInfo info : otherUsers) {
- final int colorRes = count++ % 2 == 0 ? R.color.memory_user_light
- : R.color.memory_user_dark;
- final StorageItemPreference userPref = new StorageItemPreference(
- getContext(), info.name, colorRes, info.id);
- mItemUsers.add(userPref);
- addPreference(userPref);
- }
- }
- }
-
- final boolean isRemovable = mVolume != null ? mVolume.isRemovable() : false;
- // Always create the preference since many code rely on it existing
- mMountTogglePreference = new Preference(context);
- if (isRemovable) {
- mMountTogglePreference.setTitle(R.string.sd_eject);
- mMountTogglePreference.setSummary(R.string.sd_eject_summary);
- addPreference(mMountTogglePreference);
- }
-
- final boolean allowFormat = mVolume != null;
- if (allowFormat) {
- mFormatPreference = new Preference(context);
- mFormatPreference.setTitle(R.string.sd_format);
- mFormatPreference.setSummary(R.string.sd_format_summary);
- addPreference(mFormatPreference);
- }
-
- final IPackageManager pm = ActivityThread.getPackageManager();
- try {
- if (pm.isStorageLow()) {
- mStorageLow = new Preference(context);
- mStorageLow.setOrder(ORDER_STORAGE_LOW);
- mStorageLow.setTitle(R.string.storage_low_title);
- mStorageLow.setSummary(R.string.storage_low_summary);
- addPreference(mStorageLow);
- } else if (mStorageLow != null) {
- removePreference(mStorageLow);
- mStorageLow = null;
- }
- } catch (RemoteException e) {
- }
- }
-
- public StorageVolume getStorageVolume() {
- return mVolume;
- }
-
- private void updatePreferencesFromState() {
- // Only update for physical volumes
- if (mVolume == null) return;
-
- mMountTogglePreference.setEnabled(true);
-
- final String state = mStorageManager.getVolumeState(mVolume.getPath());
-
- if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
- mItemAvailable.setTitle(R.string.memory_available_read_only);
- } else {
- mItemAvailable.setTitle(R.string.memory_available);
- }
-
- if (Environment.MEDIA_MOUNTED.equals(state)
- || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
- mMountTogglePreference.setEnabled(true);
- mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
- mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary));
- addPreference(mUsageBarPreference);
- addPreference(mItemTotal);
- addPreference(mItemAvailable);
- } else {
- if (Environment.MEDIA_UNMOUNTED.equals(state) || Environment.MEDIA_NOFS.equals(state)
- || Environment.MEDIA_UNMOUNTABLE.equals(state)) {
- 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(mItemTotal);
- removePreference(mItemAvailable);
- }
-
- if (mUsbConnected && (UsbManager.USB_FUNCTION_MTP.equals(mUsbFunction) ||
- UsbManager.USB_FUNCTION_PTP.equals(mUsbFunction))) {
- mMountTogglePreference.setEnabled(false);
- if (Environment.MEDIA_MOUNTED.equals(state)
- || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
- mMountTogglePreference.setSummary(
- mResources.getString(R.string.mtp_ptp_mode_summary));
- }
-
- if (mFormatPreference != null) {
- mFormatPreference.setEnabled(false);
- mFormatPreference.setSummary(mResources.getString(R.string.mtp_ptp_mode_summary));
- }
- } else if (mFormatPreference != null) {
- mFormatPreference.setEnabled(mMountTogglePreference.isEnabled());
- mFormatPreference.setSummary(mResources.getString(R.string.sd_format_summary));
- }
- }
-
- public void updateApproximate(long totalSize, long availSize) {
- mItemTotal.setSummary(formatSize(totalSize));
- mItemAvailable.setSummary(formatSize(availSize));
-
- mTotalSize = totalSize;
-
- final long usedSize = totalSize - availSize;
-
- mUsageBarPreference.clear();
- mUsageBarPreference.addEntry(0, usedSize / (float) totalSize, android.graphics.Color.GRAY);
- mUsageBarPreference.commit();
-
- updatePreferencesFromState();
- }
-
- private static long totalValues(HashMap<String, Long> map, String... keys) {
- long total = 0;
- for (String key : keys) {
- if (map.containsKey(key)) {
- total += map.get(key);
- }
- }
- return total;
- }
-
- public void updateDetails(MeasurementDetails details) {
- final boolean showDetails = mVolume == null || mVolume.isPrimary();
- if (!showDetails) return;
-
- // Count caches as available space, since system manages them
- mItemTotal.setSummary(formatSize(details.totalSize));
- mItemAvailable.setSummary(formatSize(details.availSize));
-
- mUsageBarPreference.clear();
-
- updatePreference(mItemApps, details.appsSize);
-
- final long dcimSize = totalValues(details.mediaSize, Environment.DIRECTORY_DCIM,
- Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES);
- updatePreference(mItemDcim, dcimSize);
-
- final long musicSize = totalValues(details.mediaSize, Environment.DIRECTORY_MUSIC,
- Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
- Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
- updatePreference(mItemMusic, musicSize);
-
- final long downloadsSize = totalValues(details.mediaSize, Environment.DIRECTORY_DOWNLOADS);
- updatePreference(mItemDownloads, downloadsSize);
-
- updatePreference(mItemCache, details.cacheSize);
- updatePreference(mItemMisc, details.miscSize);
-
- for (StorageItemPreference userPref : mItemUsers) {
- final long userSize = details.usersSize.get(userPref.userHandle);
- updatePreference(userPref, userSize);
- }
-
- mUsageBarPreference.commit();
- }
-
- private void updatePreference(StorageItemPreference pref, long size) {
- if (size > 0) {
- pref.setSummary(formatSize(size));
- final int order = pref.getOrder();
- mUsageBarPreference.addEntry(order, size / (float) mTotalSize, pref.color);
- } else {
- removePreference(pref);
- }
- }
-
- private void measure() {
- mMeasure.invalidate();
- mMeasure.measure();
- }
-
- public void onResume() {
- mMeasure.setReceiver(mReceiver);
- measure();
- }
-
- public void onStorageStateChanged() {
- init();
- measure();
- }
-
- public void onUsbStateChanged(boolean isUsbConnected, String usbFunction) {
- mUsbConnected = isUsbConnected;
- mUsbFunction = usbFunction;
- measure();
- }
-
- public void onMediaScannerFinished() {
- measure();
- }
-
- public void onCacheCleared() {
- measure();
- }
-
- public void onPause() {
- mMeasure.cleanUp();
- }
-
- private String formatSize(long size) {
- return Formatter.formatFileSize(getContext(), size);
- }
-
- private MeasurementReceiver mReceiver = new MeasurementReceiver() {
- @Override
- public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize) {
- mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE, new long[] {
- totalSize, availSize }).sendToTarget();
- }
-
- @Override
- public void updateDetails(StorageMeasurement meas, MeasurementDetails details) {
- mUpdateHandler.obtainMessage(MSG_UI_UPDATE_DETAILS, details).sendToTarget();
- }
- };
-
- public boolean mountToggleClicked(Preference preference) {
- return preference == mMountTogglePreference;
- }
-
- public Intent intentForClick(Preference pref) {
- 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.
- final String key = pref.getKey();
- if (pref == mFormatPreference) {
- intent = new Intent(Intent.ACTION_VIEW);
- intent.setClass(getContext(), com.android.settings.MediaFormat.class);
- intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
- } else if (pref == mItemApps) {
- intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
- intent.setClass(getContext(), Settings.ManageApplicationsActivity.class);
- } else if (pref == mItemDownloads) {
- intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra(
- DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
- } else if (pref == mItemMusic) {
- intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.setType("audio/mp3");
- } else if (pref == mItemDcim) {
- intent = new Intent(Intent.ACTION_VIEW);
- intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
- // TODO Create a Videos category, MediaStore.Video.Media.EXTERNAL_CONTENT_URI
- intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
- } else if (pref == mItemMisc) {
- Context context = getContext().getApplicationContext();
- intent = new Intent(context, MiscFilesHandler.class);
- intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
- }
-
- return intent;
- }
-
- public static class PreferenceHeader extends Preference {
- public PreferenceHeader(Context context, int titleRes) {
- super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
- setTitle(titleRes);
- }
-
- public PreferenceHeader(Context context, CharSequence title) {
- super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
- setTitle(title);
- }
-
- @Override
- public boolean isEnabled() {
- return false;
- }
- }
-
- /**
- * Return list of other users, excluding the current user.
- */
- private List<UserInfo> getUsersExcluding(UserInfo excluding) {
- final List<UserInfo> users = mUserManager.getUsers();
- final Iterator<UserInfo> i = users.iterator();
- while (i.hasNext()) {
- if (i.next().id == excluding.id) {
- i.remove();
- }
- }
- return users;
- }
-}
diff --git a/src/com/android/settings/search/Ranking.java b/src/com/android/settings/search/Ranking.java
index f37e1fc..2143d0d 100644
--- a/src/com/android/settings/search/Ranking.java
+++ b/src/com/android/settings/search/Ranking.java
@@ -32,7 +32,7 @@ import com.android.settings.WirelessSettings;
import com.android.settings.accessibility.AccessibilitySettings;
import com.android.settings.applications.AdvancedAppSettings;
import com.android.settings.bluetooth.BluetoothSettings;
-import com.android.settings.deviceinfo.Memory;
+import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.deviceinfo.UsbSettings;
import com.android.settings.fuelgauge.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
@@ -129,7 +129,7 @@ public final class Ranking {
sRankMap.put(ZenModeAutomationSettings.class.getName(), RANK_NOTIFICATIONS);
// Storage
- sRankMap.put(Memory.class.getName(), RANK_STORAGE);
+ sRankMap.put(StorageSettings.class.getName(), RANK_STORAGE);
sRankMap.put(UsbSettings.class.getName(), RANK_STORAGE);
// Battery
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index dae8860..b7bb062 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -34,7 +34,7 @@ import com.android.settings.WirelessSettings;
import com.android.settings.accessibility.AccessibilitySettings;
import com.android.settings.applications.AdvancedAppSettings;
import com.android.settings.bluetooth.BluetoothSettings;
-import com.android.settings.deviceinfo.Memory;
+import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.deviceinfo.UsbSettings;
import com.android.settings.fuelgauge.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
@@ -170,11 +170,11 @@ public final class SearchIndexableResources {
ZenModePrioritySettings.class.getName(),
R.drawable.ic_settings_notifications));
- sResMap.put(Memory.class.getName(),
+ sResMap.put(StorageSettings.class.getName(),
new SearchIndexableResource(
- Ranking.getRankForClassName(Memory.class.getName()),
+ Ranking.getRankForClassName(StorageSettings.class.getName()),
NO_DATA_RES_ID,
- Memory.class.getName(),
+ StorageSettings.class.getName(),
R.drawable.ic_settings_storage));
sResMap.put(UsbSettings.class.getName(),