diff options
Diffstat (limited to 'src')
13 files changed, 1382 insertions, 1484 deletions
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(), |