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:
@@ -27,10 +27,22 @@
|
||||
<item
|
||||
android:id="@+id/storage_format"
|
||||
android:title="@string/storage_menu_format" />
|
||||
<item
|
||||
android:id="@+id/storage_format_as_portable"
|
||||
android:title="@string/storage_menu_format_public"
|
||||
android:visible="false" />
|
||||
<item
|
||||
android:id="@+id/storage_format_as_internal"
|
||||
android:title="@string/storage_menu_format_private"
|
||||
android:visible="false" />
|
||||
<item
|
||||
android:id="@+id/storage_migrate"
|
||||
android:title="@string/storage_menu_migrate" />
|
||||
<item
|
||||
android:id="@+id/storage_free"
|
||||
android:title="@string/storage_menu_free" />
|
||||
<item
|
||||
android:id="@+id/storage_forget"
|
||||
android:title="@string/storage_menu_forget"
|
||||
android:visible="false" />
|
||||
</menu>
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -1,128 +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 static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
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 org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class PrivateVolumeOptionMenuControllerTest {
|
||||
|
||||
@Mock
|
||||
private MenuItem mMigrateMenuItem;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Menu mMenu;
|
||||
@Mock
|
||||
private MenuInflater mMenuInflater;
|
||||
@Mock
|
||||
private PackageManager mPm;
|
||||
@Mock
|
||||
private VolumeInfo mVolumeInfo;
|
||||
@Mock
|
||||
private VolumeInfo mPrimaryInfo;
|
||||
|
||||
private PrivateVolumeOptionMenuController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
|
||||
when(mVolumeInfo.isMountedWritable()).thenReturn(true);
|
||||
when(mPrimaryInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
|
||||
when(mMenu.findItem(anyInt())).thenReturn(mMigrateMenuItem);
|
||||
when(mMigrateMenuItem.getItemId()).thenReturn(100);
|
||||
|
||||
mController = new PrivateVolumeOptionMenuController(
|
||||
Robolectric.setupActivity(Activity.class), mPrimaryInfo, mPm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateDataMenuItemIsAdded() {
|
||||
mController.onCreateOptionsMenu(mMenu, mMenuInflater);
|
||||
|
||||
verify(mMenu).add(Menu.NONE, 100, Menu.NONE, R.string.storage_menu_migrate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateDataIsNotVisibleNormally() {
|
||||
when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mPrimaryInfo);
|
||||
when(mPrimaryInfo.isMountedWritable()).thenReturn(true);
|
||||
|
||||
mController.onCreateOptionsMenu(mMenu, mMenuInflater);
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mMigrateMenuItem).setVisible(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateDataIsVisibleWhenExternalVolumeIsPrimary() {
|
||||
when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mVolumeInfo);
|
||||
|
||||
mController.onCreateOptionsMenu(mMenu, mMenuInflater);
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mMigrateMenuItem).setVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateDataIsNotVisibleWhenExternalVolumeIsNotMounted() {
|
||||
when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mVolumeInfo);
|
||||
when(mVolumeInfo.isMountedWritable()).thenReturn(false);
|
||||
|
||||
mController.onCreateOptionsMenu(mMenu, mMenuInflater);
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mMigrateMenuItem).setVisible(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateDataGoesToMigrateWizard() {
|
||||
when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mVolumeInfo);
|
||||
|
||||
mController.onCreateOptionsMenu(mMenu, mMenuInflater);
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
assertThat(mController.onOptionsItemSelected(mMigrateMenuItem)).isTrue();
|
||||
ShadowApplication shadowApplication = ShadowApplication.getInstance();
|
||||
assertThat(shadowApplication).isNotNull();
|
||||
assertThat(shadowApplication.getNextStartedActivity().getComponent().getClassName())
|
||||
.isEqualTo(StorageWizardMigrateConfirm.class.getName());
|
||||
}
|
||||
}
|
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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 static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.storage.DiskInfo;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.os.storage.VolumeInfo;
|
||||
import android.os.storage.VolumeRecord;
|
||||
import android.view.Menu;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.deviceinfo.storage.StorageEntry;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class VolumeOptionMenuControllerTest {
|
||||
|
||||
private static final String INTERNAL_VOLUME_ID = "1";
|
||||
private static final String EXTERNAL_VOLUME_ID = "2";
|
||||
private static final String DISK_ID = "3";
|
||||
private static final String VOLUME_RECORD_FSUUID = "volume_record_fsuuid";
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Menu mMenu;
|
||||
@Mock private PackageManager mPackageManager;
|
||||
@Mock private StorageManager mStorageManager;
|
||||
@Mock private VolumeInfo mExternalVolumeInfo;
|
||||
@Mock private VolumeInfo mInternalVolumeInfo;
|
||||
|
||||
private Context mContext;
|
||||
private VolumeOptionMenuController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mContext.getSystemService(StorageManager.class)).thenReturn(mStorageManager);
|
||||
|
||||
when(mInternalVolumeInfo.getId()).thenReturn(INTERNAL_VOLUME_ID);
|
||||
when(mInternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
|
||||
when(mInternalVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
|
||||
when(mInternalVolumeInfo.isMountedWritable()).thenReturn(true);
|
||||
when(mExternalVolumeInfo.getId()).thenReturn(EXTERNAL_VOLUME_ID);
|
||||
|
||||
final StorageEntry selectedStorageEntry = new StorageEntry(mContext, mInternalVolumeInfo);
|
||||
mController = new VolumeOptionMenuController(mContext, mock(Fragment.class),
|
||||
selectedStorageEntry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPrepareOptionsMenu_unSupportedDiskInfo_formatIsVisible() {
|
||||
final StorageEntry unsupportedStorageEntry =
|
||||
new StorageEntry(new DiskInfo(DISK_ID, 0 /* flags */));
|
||||
mController.setSelectedStorageEntry(unsupportedStorageEntry);
|
||||
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mController.mFormat, atLeastOnce()).setVisible(true);
|
||||
verify(mController.mRename, never()).setVisible(true);
|
||||
verify(mController.mMount, never()).setVisible(true);
|
||||
verify(mController.mUnmount, never()).setVisible(true);
|
||||
verify(mController.mFormatAsPortable, never()).setVisible(true);
|
||||
verify(mController.mFormatAsInternal, never()).setVisible(true);
|
||||
verify(mController.mMigrate, never()).setVisible(true);
|
||||
verify(mController.mFree, never()).setVisible(true);
|
||||
verify(mController.mForget, never()).setVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPrepareOptionsMenu_missingVolumeRecord_forgetIsVisible() {
|
||||
final StorageEntry missingStorageEntry =
|
||||
new StorageEntry(new VolumeRecord(0 /* type */, VOLUME_RECORD_FSUUID));
|
||||
mController.setSelectedStorageEntry(missingStorageEntry);
|
||||
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mController.mForget, atLeastOnce()).setVisible(true);
|
||||
verify(mController.mRename, never()).setVisible(true);
|
||||
verify(mController.mMount, never()).setVisible(true);
|
||||
verify(mController.mUnmount, never()).setVisible(true);
|
||||
verify(mController.mFormat, never()).setVisible(true);
|
||||
verify(mController.mFormatAsPortable, never()).setVisible(true);
|
||||
verify(mController.mFormatAsInternal, never()).setVisible(true);
|
||||
verify(mController.mMigrate, never()).setVisible(true);
|
||||
verify(mController.mFree, never()).setVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPrepareOptionsMenu_unmountedStorage_mountIsVisible() {
|
||||
when(mInternalVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_UNMOUNTED);
|
||||
mController.setSelectedStorageEntry(new StorageEntry(mContext, mInternalVolumeInfo));
|
||||
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mController.mMount, atLeastOnce()).setVisible(true);
|
||||
verify(mController.mRename, never()).setVisible(true);
|
||||
verify(mController.mUnmount, never()).setVisible(true);
|
||||
verify(mController.mFormat, never()).setVisible(true);
|
||||
verify(mController.mFormatAsPortable, never()).setVisible(true);
|
||||
verify(mController.mFormatAsInternal, never()).setVisible(true);
|
||||
verify(mController.mMigrate, never()).setVisible(true);
|
||||
verify(mController.mFree, never()).setVisible(true);
|
||||
verify(mController.mForget, never()).setVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPrepareOptionsMenu_privateNotDefaultInternal_someMenusAreVisible() {
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mController.mRename, atLeastOnce()).setVisible(true);
|
||||
verify(mController.mUnmount, atLeastOnce()).setVisible(true);
|
||||
verify(mController.mFormatAsPortable, atLeastOnce()).setVisible(true);
|
||||
verify(mController.mMount, never()).setVisible(true);
|
||||
verify(mController.mFormat, never()).setVisible(true);
|
||||
verify(mController.mFormatAsInternal, never()).setVisible(true);
|
||||
verify(mController.mFree, never()).setVisible(true);
|
||||
verify(mController.mForget, never()).setVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPrepareOptionsMenu_privateDefaultInternal_mostMenusAreNotVisible() {
|
||||
when(mInternalVolumeInfo.getId()).thenReturn(VolumeInfo.ID_PRIVATE_INTERNAL);
|
||||
when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mInternalVolumeInfo);
|
||||
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mController.mRename, never()).setVisible(true);
|
||||
verify(mController.mUnmount, never()).setVisible(true);
|
||||
verify(mController.mFormatAsPortable, never()).setVisible(true);
|
||||
verify(mController.mMount, never()).setVisible(true);
|
||||
verify(mController.mFormat, never()).setVisible(true);
|
||||
verify(mController.mFormatAsInternal, never()).setVisible(true);
|
||||
verify(mController.mFree, never()).setVisible(true);
|
||||
verify(mController.mForget, never()).setVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPrepareOptionsMenu_publicStorage_someMenusArcVisible() {
|
||||
when(mExternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PUBLIC);
|
||||
when(mExternalVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
|
||||
when(mExternalVolumeInfo.getDiskId()).thenReturn(DISK_ID);
|
||||
final DiskInfo externalDiskInfo = mock(DiskInfo.class);
|
||||
when(mStorageManager.findDiskById(DISK_ID)).thenReturn(externalDiskInfo);
|
||||
mController.setSelectedStorageEntry(new StorageEntry(mContext, mExternalVolumeInfo));
|
||||
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mController.mRename, atLeastOnce()).setVisible(true);
|
||||
verify(mController.mUnmount, atLeastOnce()).setVisible(true);
|
||||
verify(mController.mFormat, atLeastOnce()).setVisible(true);
|
||||
verify(mController.mMount, never()).setVisible(true);
|
||||
verify(mController.mFormatAsPortable, never()).setVisible(true);
|
||||
verify(mController.mFormatAsInternal, never()).setVisible(true);
|
||||
verify(mController.mFree, never()).setVisible(true);
|
||||
verify(mController.mForget, never()).setVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPrepareOptionsMenu_noExternalStorage_migrateNotVisible() {
|
||||
when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mInternalVolumeInfo);
|
||||
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mController.mMigrate, atLeastOnce()).setVisible(false);
|
||||
verify(mController.mMigrate, never()).setVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPrepareOptionsMenu_externalPrimaryStorageAvailable_migrateIsVisible() {
|
||||
when(mExternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
|
||||
when(mExternalVolumeInfo.isMountedWritable()).thenReturn(true);
|
||||
when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mExternalVolumeInfo);
|
||||
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mController.mMigrate, atLeastOnce()).setVisible(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPrepareOptionsMenu_externalUnmounted_migrateIsVisible() {
|
||||
when(mExternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
|
||||
when(mExternalVolumeInfo.isMountedWritable()).thenReturn(false);
|
||||
when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mExternalVolumeInfo);
|
||||
|
||||
mController.onPrepareOptionsMenu(mMenu);
|
||||
|
||||
verify(mController.mMigrate, atLeastOnce()).setVisible(false);
|
||||
verify(mController.mMigrate, never()).setVisible(true);
|
||||
}
|
||||
}
|
@@ -131,28 +131,28 @@ public class StorageEntryTest {
|
||||
final StorageEntry storage = new StorageEntry(mContext, volumeInfo);
|
||||
|
||||
assertThat(storage.isVolumeInfo()).isTrue();
|
||||
assertThat(storage.isUnsupportedDiskInfo()).isFalse();
|
||||
assertThat(storage.isMissingVolumeRecord()).isFalse();
|
||||
assertThat(storage.isDiskInfoUnsupported()).isFalse();
|
||||
assertThat(storage.isVolumeRecordMissed()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isUnsupportedDiskInfo_shouldReturnTrueForDiskInfo() {
|
||||
public void isDiskInfoUnsupported_shouldReturnTrueForDiskInfo() {
|
||||
final DiskInfo diskInfo = mock(DiskInfo.class);
|
||||
final StorageEntry storage = new StorageEntry(diskInfo);
|
||||
|
||||
assertThat(storage.isVolumeInfo()).isFalse();
|
||||
assertThat(storage.isUnsupportedDiskInfo()).isTrue();
|
||||
assertThat(storage.isMissingVolumeRecord()).isFalse();
|
||||
assertThat(storage.isDiskInfoUnsupported()).isTrue();
|
||||
assertThat(storage.isVolumeRecordMissed()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isMissingVolumeRecord_shouldReturnTrueForVolumeRecord() {
|
||||
public void isVolumeRecordMissed_shouldReturnTrueForVolumeRecord() {
|
||||
final VolumeRecord volumeRecord = mock(VolumeRecord.class);
|
||||
final StorageEntry storage = new StorageEntry(volumeRecord);
|
||||
|
||||
assertThat(storage.isVolumeInfo()).isFalse();
|
||||
assertThat(storage.isUnsupportedDiskInfo()).isFalse();
|
||||
assertThat(storage.isMissingVolumeRecord()).isTrue();
|
||||
assertThat(storage.isDiskInfoUnsupported()).isFalse();
|
||||
assertThat(storage.isVolumeRecordMissed()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Reference in New Issue
Block a user