- Extend preference_category to support summary text - Removed work_profile_category, used by account list - align prefs in storage settings - Use Preference instead of PrefScreen in xmls - Use proper footer preference in NightDisplaySettings, BatterySaver, DataSaver, AutomaticStorageManage - Remove WallOfTextPreference Bug: 33775510 Test: visual Change-Id: I28156b857ff5acac8746c42384dc03d5997ebd9d
936 lines
36 KiB
Java
936 lines
36 KiB
Java
/*
|
|
* 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.app.AlertDialog;
|
|
import android.app.Dialog;
|
|
import android.app.Fragment;
|
|
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.os.storage.VolumeRecord;
|
|
import android.provider.DocumentsContract;
|
|
import android.support.v7.preference.Preference;
|
|
import android.support.v7.preference.PreferenceCategory;
|
|
import android.support.v7.preference.PreferenceGroup;
|
|
import android.support.v7.preference.PreferenceScreen;
|
|
import android.text.TextUtils;
|
|
import android.text.format.Formatter;
|
|
import android.text.format.Formatter.BytesResult;
|
|
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.nano.MetricsProto.MetricsEvent;
|
|
import com.android.settings.R;
|
|
import com.android.settings.Settings.StorageUseActivity;
|
|
import com.android.settings.SettingsPreferenceFragment;
|
|
import com.android.settings.Utils;
|
|
import com.android.settings.applications.ManageApplications;
|
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
|
import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
|
|
import com.android.settings.deviceinfo.StorageSettings.MountTask;
|
|
import com.android.settingslib.deviceinfo.StorageMeasurement;
|
|
import com.android.settingslib.deviceinfo.StorageMeasurement.MeasurementDetails;
|
|
import com.android.settingslib.deviceinfo.StorageMeasurement.MeasurementReceiver;
|
|
|
|
import com.google.android.collect.Lists;
|
|
|
|
import java.util.HashMap;
|
|
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
|
|
// TODO: warn when mounted read-only
|
|
|
|
private static final String TAG = "PrivateVolumeSettings";
|
|
private static final boolean LOGV = false;
|
|
|
|
private static final String TAG_RENAME = "rename";
|
|
private static final String TAG_OTHER_INFO = "otherInfo";
|
|
private static final String TAG_SYSTEM_INFO = "systemInfo";
|
|
private static final String TAG_USER_INFO = "userInfo";
|
|
private static final String TAG_CONFIRM_CLEAR_CACHE = "confirmClearCache";
|
|
|
|
private static final String EXTRA_VOLUME_SIZE = "volume_size";
|
|
|
|
private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
|
|
|
|
private static final int[] ITEMS_NO_SHOW_SHARED = new int[] {
|
|
R.string.storage_detail_apps,
|
|
R.string.storage_detail_system,
|
|
};
|
|
|
|
private static final int[] ITEMS_SHOW_SHARED = new int[] {
|
|
R.string.storage_detail_apps,
|
|
R.string.storage_detail_images,
|
|
R.string.storage_detail_videos,
|
|
R.string.storage_detail_audio,
|
|
R.string.storage_detail_system,
|
|
R.string.storage_detail_other,
|
|
};
|
|
|
|
private StorageManager mStorageManager;
|
|
private UserManager mUserManager;
|
|
|
|
private String mVolumeId;
|
|
private VolumeInfo mVolume;
|
|
private VolumeInfo mSharedVolume;
|
|
private long mTotalSize;
|
|
private long mSystemSize;
|
|
|
|
private StorageMeasurement mMeasure;
|
|
|
|
private UserInfo mCurrentUser;
|
|
|
|
private StorageSummaryPreference mSummary;
|
|
private List<StorageItemPreference> mItemPreferencePool = Lists.newArrayList();
|
|
private List<PreferenceCategory> mHeaderPreferencePool = Lists.newArrayList();
|
|
private int mHeaderPoolIndex;
|
|
private int mItemPoolIndex;
|
|
|
|
private Preference mExplore;
|
|
private Preference mAutomaticStorageManagement;
|
|
|
|
private boolean mNeedsUpdate;
|
|
|
|
private boolean isVolumeValid() {
|
|
return (mVolume != null) && (mVolume.getType() == VolumeInfo.TYPE_PRIVATE)
|
|
&& mVolume.isMountedReadable();
|
|
}
|
|
|
|
public PrivateVolumeSettings() {
|
|
setRetainInstance(true);
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.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);
|
|
|
|
mVolumeId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID);
|
|
mVolume = mStorageManager.findVolumeById(mVolumeId);
|
|
|
|
final long sharedDataSize = mVolume.getPath().getTotalSpace();
|
|
mTotalSize = getArguments().getLong(EXTRA_VOLUME_SIZE, 0);
|
|
mSystemSize = mTotalSize - sharedDataSize;
|
|
if (LOGV) Log.v(TAG,
|
|
"onCreate() mTotalSize: " + mTotalSize + " sharedDataSize: " + sharedDataSize);
|
|
|
|
if (mTotalSize <= 0) {
|
|
mTotalSize = sharedDataSize;
|
|
mSystemSize = 0;
|
|
}
|
|
|
|
// Find the emulated shared storage layered above this private volume
|
|
mSharedVolume = mStorageManager.findEmulatedForPrivate(mVolume);
|
|
|
|
mMeasure = new StorageMeasurement(context, mVolume, mSharedVolume);
|
|
mMeasure.setReceiver(mReceiver);
|
|
|
|
if (!isVolumeValid()) {
|
|
getActivity().finish();
|
|
return;
|
|
}
|
|
|
|
addPreferencesFromResource(R.xml.device_info_storage_volume);
|
|
getPreferenceScreen().setOrderingAsAdded(true);
|
|
|
|
mSummary = new StorageSummaryPreference(getPrefContext());
|
|
mCurrentUser = mUserManager.getUserInfo(UserHandle.myUserId());
|
|
|
|
mExplore = buildAction(R.string.storage_menu_explore);
|
|
mAutomaticStorageManagement = buildAction(R.string.storage_menu_manage);
|
|
|
|
mNeedsUpdate = true;
|
|
|
|
setHasOptionsMenu(true);
|
|
}
|
|
|
|
private void setTitle() {
|
|
getActivity().setTitle(mStorageManager.getBestVolumeDescription(mVolume));
|
|
}
|
|
|
|
private void update() {
|
|
if (!isVolumeValid()) {
|
|
getActivity().finish();
|
|
return;
|
|
}
|
|
|
|
setTitle();
|
|
|
|
// Valid options may have changed
|
|
getFragmentManager().invalidateOptionsMenu();
|
|
|
|
final Context context = getActivity();
|
|
final PreferenceScreen screen = getPreferenceScreen();
|
|
|
|
screen.removeAll();
|
|
|
|
if (getResources().getBoolean(R.bool.config_storage_manager_settings_enabled)) {
|
|
addPreference(screen, mAutomaticStorageManagement);
|
|
}
|
|
addPreference(screen, mSummary);
|
|
|
|
List<UserInfo> allUsers = mUserManager.getUsers();
|
|
final int userCount = allUsers.size();
|
|
final boolean showHeaders = userCount > 1;
|
|
final boolean showShared = (mSharedVolume != null) && mSharedVolume.isMountedReadable();
|
|
|
|
mItemPoolIndex = 0;
|
|
mHeaderPoolIndex = 0;
|
|
|
|
int addedUserCount = 0;
|
|
// Add current user and its profiles first
|
|
for (int userIndex = 0; userIndex < userCount; ++userIndex) {
|
|
final UserInfo userInfo = allUsers.get(userIndex);
|
|
if (isProfileOf(mCurrentUser, userInfo)) {
|
|
final PreferenceGroup details = showHeaders ?
|
|
addCategory(screen, userInfo.name) : screen;
|
|
addDetailItems(details, showShared, userInfo.id);
|
|
++addedUserCount;
|
|
}
|
|
}
|
|
|
|
// Add rest of users
|
|
if (userCount - addedUserCount > 0) {
|
|
PreferenceGroup otherUsers = addCategory(screen,
|
|
getText(R.string.storage_other_users));
|
|
for (int userIndex = 0; userIndex < userCount; ++userIndex) {
|
|
final UserInfo userInfo = allUsers.get(userIndex);
|
|
if (!isProfileOf(mCurrentUser, userInfo)) {
|
|
addItem(otherUsers, /* titleRes */ 0, userInfo.name, userInfo.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
addItem(screen, R.string.storage_detail_cached, null, UserHandle.USER_NULL);
|
|
|
|
if (showShared) {
|
|
addPreference(screen, mExplore);
|
|
}
|
|
|
|
final long freeBytes = mVolume.getPath().getFreeSpace();
|
|
final long usedBytes = mTotalSize - freeBytes;
|
|
|
|
if (LOGV) Log.v(TAG, "update() freeBytes: " + freeBytes + " usedBytes: " + usedBytes);
|
|
|
|
final BytesResult result = Formatter.formatBytes(getResources(), usedBytes, 0);
|
|
mSummary.setTitle(TextUtils.expandTemplate(getText(R.string.storage_size_large),
|
|
result.value, result.units));
|
|
mSummary.setSummary(getString(R.string.storage_volume_used,
|
|
Formatter.formatFileSize(context, mTotalSize)));
|
|
mSummary.setPercent(usedBytes, mTotalSize);
|
|
|
|
mMeasure.forceMeasure();
|
|
mNeedsUpdate = false;
|
|
}
|
|
|
|
private void addPreference(PreferenceGroup group, Preference pref) {
|
|
pref.setOrder(Preference.DEFAULT_ORDER);
|
|
group.addPreference(pref);
|
|
}
|
|
|
|
private PreferenceCategory addCategory(PreferenceGroup group, CharSequence title) {
|
|
PreferenceCategory category;
|
|
if (mHeaderPoolIndex < mHeaderPreferencePool.size()) {
|
|
category = mHeaderPreferencePool.get(mHeaderPoolIndex);
|
|
} else {
|
|
category = new PreferenceCategory(getPrefContext());
|
|
mHeaderPreferencePool.add(category);
|
|
}
|
|
category.setTitle(title);
|
|
category.removeAll();
|
|
addPreference(group, category);
|
|
++mHeaderPoolIndex;
|
|
return category;
|
|
}
|
|
|
|
private void addDetailItems(PreferenceGroup category, boolean showShared, int userId) {
|
|
final int[] itemsToAdd = (showShared ? ITEMS_SHOW_SHARED : ITEMS_NO_SHOW_SHARED);
|
|
for (int i = 0; i < itemsToAdd.length; ++i) {
|
|
addItem(category, itemsToAdd[i], null, userId);
|
|
}
|
|
}
|
|
|
|
private void addItem(PreferenceGroup group, int titleRes, CharSequence title, int userId) {
|
|
if (titleRes == R.string.storage_detail_system) {
|
|
if (mSystemSize <= 0) {
|
|
Log.w(TAG, "Skipping System storage because its size is " + mSystemSize);
|
|
return;
|
|
}
|
|
if (userId != UserHandle.myUserId()) {
|
|
// Only display system on current user.
|
|
return;
|
|
}
|
|
}
|
|
StorageItemPreference item;
|
|
if (mItemPoolIndex < mItemPreferencePool.size()) {
|
|
item = mItemPreferencePool.get(mItemPoolIndex);
|
|
} else {
|
|
item = buildItem();
|
|
mItemPreferencePool.add(item);
|
|
}
|
|
if (title != null) {
|
|
item.setTitle(title);
|
|
item.setKey(title.toString());
|
|
} else {
|
|
item.setTitle(titleRes);
|
|
item.setKey(Integer.toString(titleRes));
|
|
}
|
|
item.setSummary(R.string.memory_calculating_size);
|
|
item.userHandle = userId;
|
|
addPreference(group, item);
|
|
++mItemPoolIndex;
|
|
}
|
|
|
|
private StorageItemPreference buildItem() {
|
|
final StorageItemPreference item = new StorageItemPreference(getPrefContext());
|
|
return item;
|
|
}
|
|
|
|
private Preference buildAction(int titleRes) {
|
|
final Preference pref = new Preference(getPrefContext());
|
|
pref.setTitle(titleRes);
|
|
pref.setKey(Integer.toString(titleRes));
|
|
return pref;
|
|
}
|
|
|
|
static void setVolumeSize(Bundle args, long size) {
|
|
args.putLong(EXTRA_VOLUME_SIZE, size);
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
|
|
// Refresh to verify that we haven't been formatted away
|
|
mVolume = mStorageManager.findVolumeById(mVolumeId);
|
|
if (!isVolumeValid()) {
|
|
getActivity().finish();
|
|
return;
|
|
}
|
|
|
|
mStorageManager.registerListener(mStorageListener);
|
|
|
|
if (mNeedsUpdate) {
|
|
update();
|
|
} else {
|
|
setTitle();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
super.onPause();
|
|
mStorageManager.unregisterListener(mStorageListener);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
super.onDestroy();
|
|
if (mMeasure != null) {
|
|
mMeasure.onDestroy();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
super.onCreateOptionsMenu(menu, inflater);
|
|
inflater.inflate(R.menu.storage_volume, menu);
|
|
}
|
|
|
|
@Override
|
|
public void onPrepareOptionsMenu(Menu menu) {
|
|
if (!isVolumeValid()) return;
|
|
|
|
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 migrate = menu.findItem(R.id.storage_migrate);
|
|
final MenuItem manage = menu.findItem(R.id.storage_free);
|
|
|
|
// 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.getId())) {
|
|
rename.setVisible(false);
|
|
mount.setVisible(false);
|
|
unmount.setVisible(false);
|
|
format.setVisible(false);
|
|
manage.setVisible(getResources().getBoolean(
|
|
R.bool.config_storage_manager_settings_enabled));
|
|
} else {
|
|
rename.setVisible(mVolume.getType() == VolumeInfo.TYPE_PRIVATE);
|
|
mount.setVisible(mVolume.getState() == VolumeInfo.STATE_UNMOUNTED);
|
|
unmount.setVisible(mVolume.isMountedReadable());
|
|
format.setVisible(true);
|
|
manage.setVisible(false);
|
|
}
|
|
|
|
format.setTitle(R.string.storage_menu_format_public);
|
|
|
|
// Only offer to migrate when not current storage
|
|
final VolumeInfo privateVol = getActivity().getPackageManager()
|
|
.getPrimaryStorageCurrentVolume();
|
|
migrate.setVisible((privateVol != null)
|
|
&& (privateVol.getType() == VolumeInfo.TYPE_PRIVATE)
|
|
&& !Objects.equals(mVolume, privateVol));
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
final Context context = getActivity();
|
|
final Bundle args = new Bundle();
|
|
switch (item.getItemId()) {
|
|
case R.id.storage_rename:
|
|
RenameFragment.show(this, mVolume);
|
|
return true;
|
|
case R.id.storage_mount:
|
|
new MountTask(context, mVolume).execute();
|
|
return true;
|
|
case R.id.storage_unmount:
|
|
args.putString(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId());
|
|
startFragment(this, PrivateVolumeUnmount.class.getCanonicalName(),
|
|
R.string.storage_menu_unmount, 0, args);
|
|
return true;
|
|
case R.id.storage_format:
|
|
args.putString(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId());
|
|
startFragment(this, PrivateVolumeFormat.class.getCanonicalName(),
|
|
R.string.storage_menu_format, 0, args);
|
|
return true;
|
|
case R.id.storage_migrate:
|
|
final Intent intent = new Intent(context, StorageWizardMigrateConfirm.class);
|
|
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mVolume.getId());
|
|
startActivity(intent);
|
|
return true;
|
|
case R.id.storage_free:
|
|
final Intent deletion_helper_intent =
|
|
new Intent(StorageManager.ACTION_MANAGE_STORAGE);
|
|
startActivity(deletion_helper_intent);
|
|
return true;
|
|
}
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
|
|
@Override
|
|
public boolean onPreferenceTreeClick(Preference pref) {
|
|
// TODO: launch better intents for specific volume
|
|
|
|
final int userId = (pref instanceof StorageItemPreference ?
|
|
((StorageItemPreference) pref).userHandle : -1);
|
|
int itemTitleId;
|
|
try {
|
|
itemTitleId = Integer.parseInt(pref.getKey());
|
|
} catch (NumberFormatException e) {
|
|
itemTitleId = 0;
|
|
}
|
|
Intent intent = null;
|
|
switch (itemTitleId) {
|
|
case R.string.storage_detail_apps: {
|
|
Bundle args = new Bundle();
|
|
args.putString(ManageApplications.EXTRA_CLASSNAME,
|
|
StorageUseActivity.class.getName());
|
|
args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
|
|
args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription());
|
|
intent = Utils.onBuildStartFragmentIntent(getActivity(),
|
|
ManageApplications.class.getName(), args, null, R.string.apps_storage, null,
|
|
false);
|
|
|
|
} break;
|
|
case R.string.storage_detail_images: {
|
|
intent = new Intent(DocumentsContract.ACTION_BROWSE);
|
|
intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "images_root"));
|
|
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
|
|
|
} break;
|
|
case R.string.storage_detail_videos: {
|
|
intent = new Intent(DocumentsContract.ACTION_BROWSE);
|
|
intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "videos_root"));
|
|
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
|
|
|
} break;
|
|
case R.string.storage_detail_audio: {
|
|
intent = new Intent(DocumentsContract.ACTION_BROWSE);
|
|
intent.setData(DocumentsContract.buildRootUri(AUTHORITY_MEDIA, "audio_root"));
|
|
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
|
|
|
} break;
|
|
case R.string.storage_detail_system: {
|
|
SystemInfoFragment.show(this);
|
|
return true;
|
|
|
|
}
|
|
case R.string.storage_detail_other: {
|
|
OtherInfoFragment.show(this, mStorageManager.getBestVolumeDescription(mVolume),
|
|
mSharedVolume, userId);
|
|
return true;
|
|
|
|
}
|
|
case R.string.storage_detail_cached: {
|
|
ConfirmClearCacheFragment.show(this);
|
|
return true;
|
|
|
|
}
|
|
case R.string.storage_menu_explore: {
|
|
intent = mSharedVolume.buildBrowseIntent();
|
|
} break;
|
|
case R.string.storage_menu_manage: {
|
|
startFragment(this, AutomaticStorageManagerSettings.class.getCanonicalName(),
|
|
R.string.automatic_storage_manager_settings, 0, null);
|
|
return true;
|
|
}
|
|
case 0: {
|
|
UserInfoFragment.show(this, pref.getTitle(), pref.getSummary());
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (intent != null) {
|
|
intent.putExtra(Intent.EXTRA_USER_ID, userId);
|
|
|
|
launchIntent(this, intent);
|
|
return true;
|
|
}
|
|
return super.onPreferenceTreeClick(pref);
|
|
}
|
|
|
|
private final MeasurementReceiver mReceiver = new MeasurementReceiver() {
|
|
@Override
|
|
public void onDetailsChanged(MeasurementDetails details) {
|
|
updateDetails(details);
|
|
}
|
|
};
|
|
|
|
private void updateDetails(MeasurementDetails details) {
|
|
StorageItemPreference otherItem = null;
|
|
long accountedSize = 0;
|
|
long totalMiscSize = 0;
|
|
long totalDownloadsSize = 0;
|
|
|
|
for (int i = 0; i < mItemPoolIndex; ++i) {
|
|
StorageItemPreference item = mItemPreferencePool.get(i);
|
|
final int userId = item.userHandle;
|
|
int itemTitleId;
|
|
try {
|
|
itemTitleId = Integer.parseInt(item.getKey());
|
|
} catch (NumberFormatException e) {
|
|
itemTitleId = 0;
|
|
}
|
|
switch (itemTitleId) {
|
|
case R.string.storage_detail_system: {
|
|
updatePreference(item, mSystemSize);
|
|
accountedSize += mSystemSize;
|
|
if (LOGV) Log.v(TAG, "mSystemSize: " + mSystemSize
|
|
+ " accountedSize: " + accountedSize);
|
|
} break;
|
|
case R.string.storage_detail_apps: {
|
|
updatePreference(item, details.appsSize.get(userId));
|
|
accountedSize += details.appsSize.get(userId);
|
|
if (LOGV) Log.v(TAG, "appsSize: " + details.appsSize.get(userId)
|
|
+ " accountedSize: " + accountedSize);
|
|
} break;
|
|
case R.string.storage_detail_images: {
|
|
final long imagesSize = totalValues(details, userId,
|
|
Environment.DIRECTORY_DCIM, Environment.DIRECTORY_PICTURES);
|
|
updatePreference(item, imagesSize);
|
|
accountedSize += imagesSize;
|
|
if (LOGV) Log.v(TAG, "imagesSize: " + imagesSize
|
|
+ " accountedSize: " + accountedSize);
|
|
} break;
|
|
case R.string.storage_detail_videos: {
|
|
final long videosSize = totalValues(details, userId,
|
|
Environment.DIRECTORY_MOVIES);
|
|
updatePreference(item, videosSize);
|
|
accountedSize += videosSize;
|
|
if (LOGV) Log.v(TAG, "videosSize: " + videosSize
|
|
+ " accountedSize: " + accountedSize);
|
|
} break;
|
|
case R.string.storage_detail_audio: {
|
|
final long audioSize = totalValues(details, userId,
|
|
Environment.DIRECTORY_MUSIC,
|
|
Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
|
|
Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
|
|
updatePreference(item, audioSize);
|
|
accountedSize += audioSize;
|
|
if (LOGV) Log.v(TAG, "audioSize: " + audioSize
|
|
+ " accountedSize: " + accountedSize);
|
|
} break;
|
|
case R.string.storage_detail_other: {
|
|
final long downloadsSize = totalValues(details, userId,
|
|
Environment.DIRECTORY_DOWNLOADS);
|
|
final long miscSize = details.miscSize.get(userId);
|
|
totalDownloadsSize += downloadsSize;
|
|
totalMiscSize += miscSize;
|
|
accountedSize += miscSize + downloadsSize;
|
|
|
|
if (LOGV)
|
|
Log.v(TAG, "miscSize for " + userId + ": " + miscSize + "(total: "
|
|
+ totalMiscSize + ") \ndownloadsSize: " + downloadsSize + "(total: "
|
|
+ totalDownloadsSize + ") accountedSize: " + accountedSize);
|
|
|
|
// Cannot display 'Other' until all known items are accounted for.
|
|
otherItem = item;
|
|
} break;
|
|
case R.string.storage_detail_cached: {
|
|
updatePreference(item, details.cacheSize);
|
|
accountedSize += details.cacheSize;
|
|
if (LOGV)
|
|
Log.v(TAG, "cacheSize: " + details.cacheSize + " accountedSize: "
|
|
+ accountedSize);
|
|
} break;
|
|
case 0: {
|
|
final long userSize = details.usersSize.get(userId);
|
|
updatePreference(item, userSize);
|
|
accountedSize += userSize;
|
|
if (LOGV) Log.v(TAG, "userSize: " + userSize
|
|
+ " accountedSize: " + accountedSize);
|
|
} break;
|
|
}
|
|
}
|
|
if (otherItem != null) {
|
|
final long usedSize = mTotalSize - details.availSize;
|
|
final long unaccountedSize = usedSize - accountedSize;
|
|
final long otherSize = totalMiscSize + totalDownloadsSize + unaccountedSize;
|
|
if (LOGV)
|
|
Log.v(TAG, "Other items: \n\tmTotalSize: " + mTotalSize + " availSize: "
|
|
+ details.availSize + " usedSize: " + usedSize + "\n\taccountedSize: "
|
|
+ accountedSize + " unaccountedSize size: " + unaccountedSize
|
|
+ "\n\ttotalMiscSize: " + totalMiscSize + " totalDownloadsSize: "
|
|
+ totalDownloadsSize + "\n\tdetails: " + details);
|
|
updatePreference(otherItem, otherSize);
|
|
}
|
|
}
|
|
|
|
private void updatePreference(StorageItemPreference pref, long size) {
|
|
pref.setStorageSize(size, mTotalSize);
|
|
}
|
|
|
|
private boolean isProfileOf(UserInfo user, UserInfo profile) {
|
|
return user.id == profile.id ||
|
|
(user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
|
|
&& user.profileGroupId == profile.profileGroupId);
|
|
}
|
|
|
|
private static long totalValues(MeasurementDetails details, int userId, String... keys) {
|
|
long total = 0;
|
|
HashMap<String, Long> map = details.mediaSize.get(userId);
|
|
if (map != null) {
|
|
for (String key : keys) {
|
|
if (map.containsKey(key)) {
|
|
total += map.get(key);
|
|
}
|
|
}
|
|
} else {
|
|
Log.w(TAG, "MeasurementDetails mediaSize array does not have key for user " + userId);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
private static void launchIntent(Fragment fragment, Intent intent) {
|
|
try {
|
|
final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
|
|
|
|
if (userId == -1) {
|
|
fragment.startActivity(intent);
|
|
} else {
|
|
fragment.getActivity().startActivityAsUser(intent, new UserHandle(userId));
|
|
}
|
|
} catch (ActivityNotFoundException e) {
|
|
Log.w(TAG, "No activity found for " + intent);
|
|
}
|
|
}
|
|
|
|
private final StorageEventListener mStorageListener = new StorageEventListener() {
|
|
@Override
|
|
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
|
|
if (Objects.equals(mVolume.getId(), vol.getId())) {
|
|
mVolume = vol;
|
|
update();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onVolumeRecordChanged(VolumeRecord rec) {
|
|
if (Objects.equals(mVolume.getFsUuid(), rec.getFsUuid())) {
|
|
mVolume = mStorageManager.findVolumeById(mVolumeId);
|
|
update();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Dialog that allows editing of volume nickname.
|
|
*/
|
|
public static class RenameFragment extends InstrumentedDialogFragment {
|
|
public static void show(PrivateVolumeSettings parent, VolumeInfo vol) {
|
|
if (!parent.isAdded()) return;
|
|
|
|
final RenameFragment dialog = new RenameFragment();
|
|
dialog.setTargetFragment(parent, 0);
|
|
final Bundle args = new Bundle();
|
|
args.putString(VolumeRecord.EXTRA_FS_UUID, vol.getFsUuid());
|
|
dialog.setArguments(args);
|
|
dialog.show(parent.getFragmentManager(), TAG_RENAME);
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.DIALOG_VOLUME_RENAME;
|
|
}
|
|
|
|
@Override
|
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
final Context context = getActivity();
|
|
final StorageManager storageManager = context.getSystemService(StorageManager.class);
|
|
|
|
final String fsUuid = getArguments().getString(VolumeRecord.EXTRA_FS_UUID);
|
|
final VolumeInfo vol = storageManager.findVolumeByUuid(fsUuid);
|
|
final VolumeRecord rec = storageManager.findRecordByUuid(fsUuid);
|
|
|
|
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);
|
|
nickname.setText(rec.getNickname());
|
|
|
|
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: move to background thread
|
|
storageManager.setVolumeNickname(fsUuid,
|
|
nickname.getText().toString());
|
|
}
|
|
});
|
|
builder.setNegativeButton(R.string.cancel, null);
|
|
|
|
return builder.create();
|
|
}
|
|
}
|
|
|
|
public static class SystemInfoFragment extends InstrumentedDialogFragment {
|
|
public static void show(Fragment parent) {
|
|
if (!parent.isAdded()) return;
|
|
|
|
final SystemInfoFragment dialog = new SystemInfoFragment();
|
|
dialog.setTargetFragment(parent, 0);
|
|
dialog.show(parent.getFragmentManager(), TAG_SYSTEM_INFO);
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.DIALOG_STORAGE_SYSTEM_INFO;
|
|
}
|
|
|
|
@Override
|
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
return new AlertDialog.Builder(getActivity())
|
|
.setMessage(R.string.storage_detail_dialog_system)
|
|
.setPositiveButton(android.R.string.ok, null)
|
|
.create();
|
|
}
|
|
}
|
|
|
|
public static class OtherInfoFragment extends InstrumentedDialogFragment {
|
|
public static void show(Fragment parent, String title, VolumeInfo sharedVol, int userId) {
|
|
if (!parent.isAdded()) return;
|
|
|
|
final OtherInfoFragment dialog = new OtherInfoFragment();
|
|
dialog.setTargetFragment(parent, 0);
|
|
final Bundle args = new Bundle();
|
|
args.putString(Intent.EXTRA_TITLE, title);
|
|
|
|
final Intent intent = sharedVol.buildBrowseIntent();
|
|
intent.putExtra(Intent.EXTRA_USER_ID, userId);
|
|
args.putParcelable(Intent.EXTRA_INTENT, intent);
|
|
dialog.setArguments(args);
|
|
dialog.show(parent.getFragmentManager(), TAG_OTHER_INFO);
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.DIALOG_STORAGE_OTHER_INFO;
|
|
}
|
|
|
|
@Override
|
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
final Context context = getActivity();
|
|
|
|
final String title = getArguments().getString(Intent.EXTRA_TITLE);
|
|
final Intent intent = getArguments().getParcelable(Intent.EXTRA_INTENT);
|
|
|
|
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
|
builder.setMessage(
|
|
TextUtils.expandTemplate(getText(R.string.storage_detail_dialog_other), title));
|
|
|
|
builder.setPositiveButton(R.string.storage_menu_explore,
|
|
new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
launchIntent(OtherInfoFragment.this, intent);
|
|
}
|
|
});
|
|
builder.setNegativeButton(android.R.string.cancel, null);
|
|
|
|
return builder.create();
|
|
}
|
|
}
|
|
|
|
public static class UserInfoFragment extends InstrumentedDialogFragment {
|
|
public static void show(Fragment parent, CharSequence userLabel, CharSequence userSize) {
|
|
if (!parent.isAdded()) return;
|
|
|
|
final UserInfoFragment dialog = new UserInfoFragment();
|
|
dialog.setTargetFragment(parent, 0);
|
|
final Bundle args = new Bundle();
|
|
args.putCharSequence(Intent.EXTRA_TITLE, userLabel);
|
|
args.putCharSequence(Intent.EXTRA_SUBJECT, userSize);
|
|
dialog.setArguments(args);
|
|
dialog.show(parent.getFragmentManager(), TAG_USER_INFO);
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.DIALOG_STORAGE_USER_INFO;
|
|
}
|
|
|
|
@Override
|
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
final Context context = getActivity();
|
|
|
|
final CharSequence userLabel = getArguments().getCharSequence(Intent.EXTRA_TITLE);
|
|
final CharSequence userSize = getArguments().getCharSequence(Intent.EXTRA_SUBJECT);
|
|
|
|
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
|
builder.setMessage(TextUtils.expandTemplate(
|
|
getText(R.string.storage_detail_dialog_user), userLabel, userSize));
|
|
|
|
builder.setPositiveButton(android.R.string.ok, null);
|
|
|
|
return builder.create();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dialog to request user confirmation before clearing all cache data.
|
|
*/
|
|
public static class ConfirmClearCacheFragment extends InstrumentedDialogFragment {
|
|
public static void show(Fragment parent) {
|
|
if (!parent.isAdded()) return;
|
|
|
|
final ConfirmClearCacheFragment dialog = new ConfirmClearCacheFragment();
|
|
dialog.setTargetFragment(parent, 0);
|
|
dialog.show(parent.getFragmentManager(), TAG_CONFIRM_CLEAR_CACHE);
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.DIALOG_STORAGE_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 UserManager um = context.getSystemService(UserManager.class);
|
|
|
|
for (int userId : um.getProfileIdsWithDisabled(context.getUserId())) {
|
|
final List<PackageInfo> infos = pm.getInstalledPackagesAsUser(0, userId);
|
|
final ClearCacheObserver observer = new ClearCacheObserver(
|
|
target, infos.size());
|
|
for (PackageInfo info : infos) {
|
|
pm.deleteApplicationCacheFilesAsUser(info.packageName, userId,
|
|
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.getActivity().runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mTarget.update();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|