Add option menu in StorageDashboardFragment

Add option menu for each kind of storage

1. Unsupported disk: Format.
2. Missing volume record: Forget.
3. Unmounted storage: Mount.
4. Default internal storage: Migrate.
5. Private volume: Rename / Unmount / Format as portable / Migrate.
6. Publuc volume: Rename / Unmount / Format / Format as internal.

Bug: 174964885
Test: atest VolumeOptionMenuControllerTest
Change-Id: I85fa117ff0a49ec7a53ba36580591c7ce7f5a8dc
Merged-In: I85fa117ff0a49ec7a53ba36580591c7ce7f5a8dc
This commit is contained in:
Arc Wang
2021-03-01 14:54:32 +08:00
parent d496a737ce
commit c22749530b
11 changed files with 791 additions and 323 deletions

View File

@@ -1,89 +0,0 @@
/*
* Copyright (C) 2017 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.content.pm.PackageManager;
import android.os.storage.VolumeInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.settings.R;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;
import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu;
import java.util.Objects;
/**
* Handles the option menu on the Storage settings.
*/
public class PrivateVolumeOptionMenuController implements LifecycleObserver, OnCreateOptionsMenu,
OnPrepareOptionsMenu, OnOptionsItemSelected {
private static final int OPTIONS_MENU_MIGRATE_DATA = 100;
private Context mContext;
private VolumeInfo mVolumeInfo;
private PackageManager mPm;
public PrivateVolumeOptionMenuController(
Context context, VolumeInfo volumeInfo, PackageManager packageManager) {
mContext = context;
mVolumeInfo = volumeInfo;
mPm = packageManager;
}
@Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
menu.add(Menu.NONE, OPTIONS_MENU_MIGRATE_DATA, 0, R.string.storage_menu_migrate);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
if (mVolumeInfo == null) {
return;
}
// Only offer to migrate when not current storage
final VolumeInfo privateVol = mPm.getPrimaryStorageCurrentVolume();
final MenuItem migrate = menu.findItem(OPTIONS_MENU_MIGRATE_DATA);
if (migrate != null) {
migrate.setVisible((privateVol != null)
&& (privateVol.getType() == VolumeInfo.TYPE_PRIVATE)
&& !Objects.equals(mVolumeInfo, privateVol)
&& privateVol.isMountedWritable());
}
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
if (menuItem.getItemId() == OPTIONS_MENU_MIGRATE_DATA) {
final Intent intent = new Intent(mContext, StorageWizardMigrateConfirm.class);
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mVolumeInfo.getId());
mContext.startActivity(intent);
return true;
}
return false;
}
public void setVolumeInfo(VolumeInfo volumeInfo) {
mVolumeInfo = volumeInfo;
}
}

View File

@@ -17,11 +17,9 @@
package com.android.settings.deviceinfo;
import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -38,26 +36,24 @@ import android.util.SparseArray;
import android.view.View;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController;
import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
import com.android.settings.deviceinfo.storage.DiskInitFragment;
import com.android.settings.deviceinfo.storage.SecondaryUserController;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
import com.android.settings.deviceinfo.storage.StorageEntry;
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
import com.android.settings.deviceinfo.storage.StorageSelectionPreferenceController;
import com.android.settings.deviceinfo.storage.StorageUsageProgressBarPreferenceController;
import com.android.settings.deviceinfo.storage.StorageUtils;
import com.android.settings.deviceinfo.storage.UserIconLoader;
import com.android.settings.deviceinfo.storage.VolumeSizesLoader;
import com.android.settings.overlay.FeatureFactory;
@@ -96,7 +92,7 @@ public class StorageDashboardFragment extends DashboardFragment
private CachedStorageValuesHelper mCachedStorageValuesHelper;
private StorageItemPreferenceController mPreferenceController;
private PrivateVolumeOptionMenuController mOptionMenuController;
private VolumeOptionMenuController mOptionMenuController;
private StorageSelectionPreferenceController mStorageSelectionController;
private StorageUsageProgressBarPreferenceController mStorageUsageProgressBarController;
private List<AbstractPreferenceController> mSecondaryUsers;
@@ -110,22 +106,31 @@ public class StorageDashboardFragment extends DashboardFragment
return;
}
final StorageEntry storageEntry = new StorageEntry(getContext(), volumeInfo);
final StorageEntry changedStorageEntry = new StorageEntry(getContext(), volumeInfo);
switch (volumeInfo.getState()) {
case VolumeInfo.STATE_MOUNTED:
case VolumeInfo.STATE_MOUNTED_READ_ONLY:
case VolumeInfo.STATE_UNMOUNTABLE:
if (!mStorageEntries.contains(storageEntry)) {
mStorageEntries.add(storageEntry);
refreshUi();
// Add mounted or unmountable storage in the list and show it on spinner.
// Unmountable storages are the storages which has a problem format and android
// is not able to mount it automatically.
// Users can format an unmountable storage by the UI and then use the storage.
mStorageEntries.removeIf(storageEntry -> {
return storageEntry.equals(changedStorageEntry);
});
mStorageEntries.add(changedStorageEntry);
if (changedStorageEntry.equals(mSelectedStorageEntry)) {
mSelectedStorageEntry = changedStorageEntry;
}
refreshUi();
break;
case VolumeInfo.STATE_REMOVED:
case VolumeInfo.STATE_UNMOUNTED:
case VolumeInfo.STATE_BAD_REMOVAL:
case VolumeInfo.STATE_EJECTING:
if (mStorageEntries.remove(storageEntry)) {
if (mSelectedStorageEntry.equals(storageEntry)) {
// Remove removed storage from list and don't show it on spinner.
if (mStorageEntries.remove(changedStorageEntry)) {
if (changedStorageEntry.equals(mSelectedStorageEntry)) {
mSelectedStorageEntry =
StorageEntry.getDefaultInternalStorageEntry(getContext());
}
@@ -139,10 +144,32 @@ public class StorageDashboardFragment extends DashboardFragment
@Override
public void onVolumeRecordChanged(VolumeRecord volumeRecord) {
final StorageEntry storageEntry = new StorageEntry(volumeRecord);
if (!mStorageEntries.contains(storageEntry)) {
mStorageEntries.add(storageEntry);
refreshUi();
if (isVolumeRecordMissed(volumeRecord)) {
// VolumeRecord is a metadata of VolumeInfo, if a VolumeInfo is missing
// (e.g., internal SD card is removed.) show the missing storage to users,
// users can insert the SD card or manually forget the storage from the device.
final StorageEntry storageEntry = new StorageEntry(volumeRecord);
if (!mStorageEntries.contains(storageEntry)) {
mStorageEntries.add(storageEntry);
refreshUi();
}
} else {
// Find mapped VolumeInfo and replace with existing one for something changed.
// (e.g., Renamed.)
final VolumeInfo mappedVolumeInfo =
mStorageManager.findVolumeByUuid(volumeRecord.getFsUuid());
if (mappedVolumeInfo == null) {
return;
}
final boolean removeMappedStorageEntry = mStorageEntries.removeIf(storageEntry ->
storageEntry.isVolumeInfo()
&& TextUtils.equals(storageEntry.getFsUuid(), volumeRecord.getFsUuid())
);
if (removeMappedStorageEntry) {
mStorageEntries.add(new StorageEntry(getContext(), mappedVolumeInfo));
refreshUi();
}
}
}
@@ -161,7 +188,7 @@ public class StorageDashboardFragment extends DashboardFragment
@Override
public void onDiskScanned(DiskInfo disk, int volumeCount) {
if (!isInteresting(disk)) {
if (!isDiskUnsupported(disk)) {
return;
}
final StorageEntry storageEntry = new StorageEntry(disk);
@@ -195,8 +222,19 @@ public class StorageDashboardFragment extends DashboardFragment
}
}
// Only interested in unsupported disk.
private static boolean isInteresting(DiskInfo disk) {
/**
* VolumeRecord is a metadata of VolumeInfo, this is the case where a VolumeInfo is missing.
* (e.g., internal SD card is removed.)
*/
private boolean isVolumeRecordMissed(VolumeRecord volumeRecord) {
return volumeRecord.getType() == VolumeInfo.TYPE_PRIVATE
&& mStorageManager.findVolumeByUuid(volumeRecord.getFsUuid()) == null;
}
/**
* A unsupported disk is the disk of problem format, android is not able to mount automatically.
*/
private static boolean isDiskUnsupported(DiskInfo disk) {
return disk.volumeCount == 0 && disk.size > 0;
}
@@ -205,7 +243,8 @@ public class StorageDashboardFragment extends DashboardFragment
mStorageSelectionController.setSelectedStorageEntry(mSelectedStorageEntry);
mStorageUsageProgressBarController.setSelectedStorageEntry(mSelectedStorageEntry);
mOptionMenuController.setVolumeInfo(mSelectedStorageEntry.getVolumeInfo());
mOptionMenuController.setSelectedStorageEntry(mSelectedStorageEntry);
getActivity().invalidateOptionsMenu();
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
@@ -263,18 +302,11 @@ public class StorageDashboardFragment extends DashboardFragment
mSelectedStorageEntry = storageEntry;
refreshUi();
if (storageEntry.isUnsupportedDiskInfo() || storageEntry.isUnmountable()) {
if (storageEntry.isDiskInfoUnsupported() || storageEntry.isUnmountable()) {
DiskInitFragment.show(this, R.string.storage_dialog_unmountable,
storageEntry.getDiskId());
} else if (storageEntry.isMissingVolumeRecord()) {
final Bundle args = new Bundle();
args.putString(VolumeRecord.EXTRA_FS_UUID, storageEntry.getFsUuid());
new SubSettingLauncher(getContext())
.setDestination(PrivateVolumeForget.class.getCanonicalName())
.setTitleRes(R.string.storage_menu_forget)
.setSourceMetricsCategory(getMetricsCategory())
.setArguments(args)
.launch();
} else if (storageEntry.isVolumeRecordMissed()) {
StorageUtils.launchForgetMissingVolumeRecordFragment(getContext(), storageEntry);
}
});
mStorageUsageProgressBarController = use(StorageUsageProgressBarPreferenceController.class);
@@ -282,9 +314,8 @@ public class StorageDashboardFragment extends DashboardFragment
@VisibleForTesting
void initializeOptionsMenu(Activity activity) {
mOptionMenuController = new PrivateVolumeOptionMenuController(activity,
mSelectedStorageEntry.getVolumeInfo(),
activity.getPackageManager());
mOptionMenuController = new VolumeOptionMenuController(activity, this,
mSelectedStorageEntry);
getSettingsLifecycle().addObserver(mOptionMenuController);
setHasOptionsMenu(true);
activity.invalidateOptionsMenu();
@@ -312,15 +343,12 @@ public class StorageDashboardFragment extends DashboardFragment
.filter(volumeInfo -> isInteresting(volumeInfo))
.map(volumeInfo -> new StorageEntry(getContext(), volumeInfo))
.collect(Collectors.toList()));
// Shows unsupported disks to give a chance to init.
mStorageEntries.addAll(mStorageManager.getDisks().stream()
.filter(disk -> isInteresting(disk))
.filter(disk -> isDiskUnsupported(disk))
.map(disk -> new StorageEntry(disk))
.collect(Collectors.toList()));
// Shows missing private volumes.
mStorageEntries.addAll(mStorageManager.getVolumeRecords().stream()
.filter(volumeRecord -> volumeRecord.getType() == VolumeInfo.TYPE_PRIVATE
&& mStorageManager.findVolumeByUuid(volumeRecord.getFsUuid()) == null)
.filter(volumeRecord -> isVolumeRecordMissed(volumeRecord))
.map(volumeRecord -> new StorageEntry(volumeRecord))
.collect(Collectors.toList()));
refreshUi();
@@ -619,52 +647,4 @@ public class StorageDashboardFragment extends DashboardFragment
onReceivedSizes();
}
}
/** A dialog which guides users to initialize a specified unsupported disk. */
public static class DiskInitFragment extends InstrumentedDialogFragment {
private static final String TAG_DISK_INIT = "disk_init";
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_VOLUME_INIT;
}
/** Shows the dialo for the specified diskId from DiskInfo. */
public static void show(Fragment parent, int resId, String diskId) {
final Bundle args = new Bundle();
args.putInt(Intent.EXTRA_TEXT, resId);
args.putString(DiskInfo.EXTRA_DISK_ID, diskId);
final DiskInitFragment dialog = new DiskInitFragment();
dialog.setArguments(args);
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_DISK_INIT);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final StorageManager storageManager = context.getSystemService(StorageManager.class);
final int resId = getArguments().getInt(Intent.EXTRA_TEXT);
final String diskId = getArguments().getString(DiskInfo.EXTRA_DISK_ID);
final DiskInfo disk = storageManager.findDiskById(diskId);
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(TextUtils.expandTemplate(getText(resId), disk.getDescription()));
builder.setPositiveButton(R.string.storage_menu_set_up,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final Intent intent = new Intent(context, StorageWizardInit.class);
intent.putExtra(DiskInfo.EXTRA_DISK_ID, diskId);
startActivity(intent);
}
});
builder.setNegativeButton(R.string.cancel, null);
return builder.create();
}
}
}

View File

@@ -0,0 +1,257 @@
/*
* Copyright (C) 2021 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.ActivityManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserManager;
import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.deviceinfo.StorageSettings.MountTask;
import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
import com.android.settings.deviceinfo.storage.StorageEntry;
import com.android.settings.deviceinfo.storage.StorageRenameFragment;
import com.android.settings.deviceinfo.storage.StorageUtils;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;
import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu;
import java.util.Objects;
/**
* Handles the option menu on the Storage settings.
*/
public class VolumeOptionMenuController implements LifecycleObserver, OnCreateOptionsMenu,
OnPrepareOptionsMenu, OnOptionsItemSelected {
@VisibleForTesting
MenuItem mRename;
@VisibleForTesting
MenuItem mMount;
@VisibleForTesting
MenuItem mUnmount;
@VisibleForTesting
MenuItem mFormat;
@VisibleForTesting
MenuItem mFormatAsPortable;
@VisibleForTesting
MenuItem mFormatAsInternal;
@VisibleForTesting
MenuItem mMigrate;
@VisibleForTesting
MenuItem mFree;
@VisibleForTesting
MenuItem mForget;
private final Context mContext;
private final Fragment mFragment;
private final PackageManager mPackageManager;
private final StorageManager mStorageManager;
private StorageEntry mStorageEntry;
public VolumeOptionMenuController(Context context, Fragment parent, StorageEntry storageEntry) {
mContext = context;
mFragment = parent;
mPackageManager = context.getPackageManager();
mStorageManager = context.getSystemService(StorageManager.class);
mStorageEntry = storageEntry;
}
@Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.storage_volume, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
mRename = menu.findItem(R.id.storage_rename);
mMount = menu.findItem(R.id.storage_mount);
mUnmount = menu.findItem(R.id.storage_unmount);
mFormat = menu.findItem(R.id.storage_format);
mFormatAsPortable = menu.findItem(R.id.storage_format_as_portable);
mFormatAsInternal = menu.findItem(R.id.storage_format_as_internal);
mMigrate = menu.findItem(R.id.storage_migrate);
mFree = menu.findItem(R.id.storage_free);
mForget = menu.findItem(R.id.storage_forget);
mRename.setVisible(false);
mMount.setVisible(false);
mUnmount.setVisible(false);
mFormat.setVisible(false);
mFormatAsPortable.setVisible(false);
mFormatAsInternal.setVisible(false);
mMigrate.setVisible(false);
mFree.setVisible(false);
mForget.setVisible(false);
if (mStorageEntry.isDiskInfoUnsupported()) {
mFormat.setVisible(true);
return;
}
if (mStorageEntry.isVolumeRecordMissed()) {
mForget.setVisible(true);
return;
}
if (mStorageEntry.isUnmounted()) {
mMount.setVisible(true);
return;
}
if (!mStorageEntry.isMounted()) {
return;
}
if (mStorageEntry.isPrivate()) {
if (!mStorageEntry.isDefaultInternalStorage()) {
mRename.setVisible(true);
mUnmount.setVisible(true);
mFormatAsPortable.setVisible(true);
}
// Only offer to migrate when not current storage.
final VolumeInfo primaryVolumeInfo = mPackageManager.getPrimaryStorageCurrentVolume();
final VolumeInfo selectedVolumeInfo = mStorageEntry.getVolumeInfo();
mMigrate.setVisible(primaryVolumeInfo != null
&& primaryVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE
&& !Objects.equals(selectedVolumeInfo, primaryVolumeInfo)
&& primaryVolumeInfo.isMountedWritable());
return;
}
if (mStorageEntry.isPublic()) {
mRename.setVisible(true);
mUnmount.setVisible(true);
mFormat.setVisible(true);
final DiskInfo diskInfo = mStorageManager.findDiskById(mStorageEntry.getDiskId());
mFormatAsInternal.setVisible(diskInfo != null
&& diskInfo.isAdoptable()
&& UserManager.get(mContext).isAdminUser()
&& !ActivityManager.isUserAMonkey());
return;
}
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
if (!mFragment.isAdded()) {
return false;
}
final int menuId = menuItem.getItemId();
if (menuId == R.id.storage_mount) {
if (mStorageEntry.isUnmounted()) {
new MountTask(mFragment.getActivity(), mStorageEntry.getVolumeInfo()).execute();
return true;
}
return false;
}
if (menuId == R.id.storage_unmount) {
if (mStorageEntry.isMounted()) {
if (mStorageEntry.isPublic()) {
new UnmountTask(mFragment.getActivity(),
mStorageEntry.getVolumeInfo()).execute();
return true;
}
if (mStorageEntry.isPrivate() && !mStorageEntry.isDefaultInternalStorage()) {
final Bundle args = new Bundle();
args.putString(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
new SubSettingLauncher(mContext)
.setDestination(PrivateVolumeUnmount.class.getCanonicalName())
.setTitleRes(R.string.storage_menu_unmount)
.setSourceMetricsCategory(SettingsEnums.DEVICEINFO_STORAGE)
.setArguments(args)
.launch();
return true;
}
}
return false;
}
if (menuId == R.id.storage_rename) {
if ((mStorageEntry.isPrivate() && !mStorageEntry.isDefaultInternalStorage())
|| mStorageEntry.isPublic()) {
StorageRenameFragment.show(mFragment, mStorageEntry.getVolumeInfo());
return true;
}
return false;
}
if (menuId == R.id.storage_format) {
if (mStorageEntry.isDiskInfoUnsupported() || mStorageEntry.isPublic()) {
StorageWizardFormatConfirm.showPublic(mFragment.getActivity(),
mStorageEntry.getDiskId());
return true;
}
return false;
}
if (menuId == R.id.storage_format_as_portable) {
if (mStorageEntry.isPrivate()) {
final Bundle args = new Bundle();
args.putString(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
new SubSettingLauncher(mContext)
.setDestination(PrivateVolumeFormat.class.getCanonicalName())
.setTitleRes(R.string.storage_menu_format)
.setSourceMetricsCategory(SettingsEnums.DEVICEINFO_STORAGE)
.setArguments(args)
.launch();
return true;
}
return false;
}
if (menuId == R.id.storage_format_as_internal) {
if (mStorageEntry.isPublic()) {
StorageWizardFormatConfirm.showPrivate(mFragment.getActivity(),
mStorageEntry.getDiskId());
return true;
}
return false;
}
if (menuId == R.id.storage_migrate) {
if (mStorageEntry.isPrivate()) {
final Intent intent = new Intent(mContext, StorageWizardMigrateConfirm.class);
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
mContext.startActivity(intent);
return true;
}
return false;
}
if (menuId == R.id.storage_forget) {
if (mStorageEntry.isVolumeRecordMissed()) {
StorageUtils.launchForgetMissingVolumeRecordFragment(mContext, mStorageEntry);
return true;
}
return false;
}
return false;
}
public void setSelectedStorageEntry(StorageEntry storageEntry) {
mStorageEntry = storageEntry;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2021 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.storage;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.text.TextUtils;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.deviceinfo.StorageWizardInit;
/** A dialog which guides users to initialize a specified unsupported disk. */
public class DiskInitFragment extends InstrumentedDialogFragment {
private static final String TAG_DISK_INIT = "disk_init";
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_VOLUME_INIT;
}
/** Shows the dialog for the specified diskId from DiskInfo. */
public static void show(Fragment parent, int resId, String diskId) {
final Bundle args = new Bundle();
args.putInt(Intent.EXTRA_TEXT, resId);
args.putString(DiskInfo.EXTRA_DISK_ID, diskId);
final DiskInitFragment dialog = new DiskInitFragment();
dialog.setArguments(args);
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_DISK_INIT);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final StorageManager storageManager = context.getSystemService(StorageManager.class);
final int resId = getArguments().getInt(Intent.EXTRA_TEXT);
final String diskId = getArguments().getString(DiskInfo.EXTRA_DISK_ID);
final DiskInfo disk = storageManager.findDiskById(diskId);
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
return builder.setMessage(TextUtils.expandTemplate(getText(resId), disk.getDescription()))
.setPositiveButton(R.string.storage_menu_set_up, (dialog, which) -> {
final Intent intent = new Intent(context, StorageWizardInit.class);
intent.putExtra(DiskInfo.EXTRA_DISK_ID, diskId);
startActivity(intent); })
.setNegativeButton(R.string.cancel, null)
.create();
}
}

View File

@@ -106,7 +106,7 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
if (isVolumeInfo()) {
return mVolumeInfo.equals(StorageEntry.mVolumeInfo);
}
if (isUnsupportedDiskInfo()) {
if (isDiskInfoUnsupported()) {
return mUnsupportedDiskInfo.equals(StorageEntry.mUnsupportedDiskInfo);
}
return mMissingVolumeRecord.equals(StorageEntry.mMissingVolumeRecord);
@@ -117,7 +117,7 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
if (isVolumeInfo()) {
return mVolumeInfo.hashCode();
}
if (isUnsupportedDiskInfo()) {
if (isDiskInfoUnsupported()) {
return mUnsupportedDiskInfo.hashCode();
}
return mMissingVolumeRecord.hashCode();
@@ -128,7 +128,7 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
if (isVolumeInfo()) {
return mVolumeInfo.toString();
}
if (isUnsupportedDiskInfo()) {
if (isDiskInfoUnsupported()) {
return mUnsupportedDiskInfo.toString();
}
return mMissingVolumeRecord.toString();
@@ -164,10 +164,10 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
return 1;
}
if (!isMissingVolumeRecord() && other.isMissingVolumeRecord()) {
if (!isVolumeRecordMissed() && other.isVolumeRecordMissed()) {
return -1;
}
if (isMissingVolumeRecord() && !other.isMissingVolumeRecord()) {
if (isVolumeRecordMissed() && !other.isVolumeRecordMissed()) {
return 1;
}
@@ -192,12 +192,12 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
}
/** If it's an unsupported DiskInfo. */
public boolean isUnsupportedDiskInfo() {
public boolean isDiskInfoUnsupported() {
return mUnsupportedDiskInfo != null;
}
/** If it's a missing VolumeRecord. */
public boolean isMissingVolumeRecord() {
public boolean isVolumeRecordMissed() {
return mMissingVolumeRecord != null;
}
@@ -216,6 +216,11 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
|| mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);
}
/** If it's an unmounted storage. */
public boolean isUnmounted() {
return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTED);
}
/** If it's an unmountable storage. */
public boolean isUnmountable() {
return mVolumeInfo == null ? false : mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTABLE;
@@ -226,12 +231,17 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE;
}
/** If it's a public storage. */
public boolean isPublic() {
return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PUBLIC;
}
/** Returns description. */
public String getDescription() {
if (isVolumeInfo()) {
return mVolumeInfoDescription;
}
if (isUnsupportedDiskInfo()) {
if (isDiskInfoUnsupported()) {
return mUnsupportedDiskInfo.getDescription();
}
return mMissingVolumeRecord.getNickname();
@@ -242,7 +252,7 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
if (isVolumeInfo()) {
return mVolumeInfo.getId();
}
if (isUnsupportedDiskInfo()) {
if (isDiskInfoUnsupported()) {
return mUnsupportedDiskInfo.getId();
}
return mMissingVolumeRecord.getFsUuid();
@@ -253,7 +263,7 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
if (isVolumeInfo()) {
return mVolumeInfo.getDiskId();
}
if (isUnsupportedDiskInfo()) {
if (isDiskInfoUnsupported()) {
return mUnsupportedDiskInfo.getId();
}
return null;
@@ -264,7 +274,7 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
if (isVolumeInfo()) {
return mVolumeInfo.getFsUuid();
}
if (isUnsupportedDiskInfo()) {
if (isDiskInfoUnsupported()) {
return null;
}
return mMissingVolumeRecord.getFsUuid();

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2021 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.storage;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
/**
* Dialog that allows editing of volume nickname.
*/
public class StorageRenameFragment extends InstrumentedDialogFragment {
private static final String TAG_RENAME = "rename";
/** Shows the rename dialog. */
public static void show(Fragment parent, VolumeInfo vol) {
final StorageRenameFragment dialog = new StorageRenameFragment();
dialog.setTargetFragment(parent, 0 /* requestCode */);
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 SettingsEnums.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 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());
return builder.setTitle(R.string.storage_rename_title)
.setView(view)
.setPositiveButton(R.string.save, (dialog, which) ->
// TODO: move to background thread
storageManager.setVolumeNickname(fsUuid, nickname.getText().toString()))
.setNegativeButton(R.string.cancel, null)
.create();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2021 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.storage;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.os.storage.VolumeRecord;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.deviceinfo.PrivateVolumeForget;
/** Storage utilities */
public class StorageUtils {
/** Launches the fragment to forget a specified missing volume record. */
public static void launchForgetMissingVolumeRecordFragment(Context context,
StorageEntry storageEntry) {
if (storageEntry == null || !storageEntry.isVolumeRecordMissed()) {
return;
}
final Bundle args = new Bundle();
args.putString(VolumeRecord.EXTRA_FS_UUID, storageEntry.getFsUuid());
new SubSettingLauncher(context)
.setDestination(PrivateVolumeForget.class.getCanonicalName())
.setTitleRes(R.string.storage_menu_forget)
.setSourceMetricsCategory(SettingsEnums.SETTINGS_STORAGE_CATEGORY)
.setArguments(args)
.launch();
}
}