Storage Settings shift profile selection tab position

In this change, Storage Settings main UI is composed by 3 fragments.

StorageDashboardFragment only shows when there is only personal profile for
current user.

ProfileSelectStorageFragment (controls preferences above profile tab) and
StorageDashboardNoHeaderFragment (controls preferences below profile tab)
only show when current user has installed work profile.

Bug: 174964885
Test: atest com.android.settings.deviceinfo
      atest com.android.settings.deviceinfo.storage
      make RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.deviceinfo
      make RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.deviceinfo.storage
Change-Id: I50dea2fbcae39a1488e648f3ca615f54840e19d5
This commit is contained in:
Arc Wang
2021-05-06 21:20:21 +08:00
parent 15d75cf59a
commit c0b5ba5225
10 changed files with 967 additions and 125 deletions

View File

@@ -16,32 +16,301 @@
package com.android.settings.dashboard.profileselector;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.os.storage.DiskInfo;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.deviceinfo.StorageCategoryFragment;
import com.android.settings.deviceinfo.VolumeOptionMenuController;
import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController;
import com.android.settings.deviceinfo.storage.DiskInitFragment;
import com.android.settings.deviceinfo.storage.StorageEntry;
import com.android.settings.deviceinfo.storage.StorageSelectionPreferenceController;
import com.android.settings.deviceinfo.storage.StorageUsageProgressBarPreferenceController;
import com.android.settings.deviceinfo.storage.StorageUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Storage Settings page for personal/managed profile.
* Storage Settings main UI is composed by 3 fragments:
*
* StorageDashboardFragment only shows when there is only personal profile for current user.
*
* ProfileSelectStorageFragment (controls preferences above profile tab) and
* StorageCategoryFragment (controls preferences below profile tab) only show when current
* user has installed work profile.
*
* ProfileSelectStorageFragment and StorageCategoryFragment have many similar or the same
* code as StorageDashboardFragment. Remember to sync code between these fragments when you have to
* change Storage Settings.
*/
public class ProfileSelectStorageFragment extends ProfileSelectFragment {
private static final String TAG = "ProfileSelStorageFrag";
private static final String SELECTED_STORAGE_ENTRY_KEY = "selected_storage_entry_key";
private StorageManager mStorageManager;
private final List<StorageEntry> mStorageEntries = new ArrayList<>();
private StorageEntry mSelectedStorageEntry;
private Fragment[] mFragments;
private StorageSelectionPreferenceController mStorageSelectionController;
private StorageUsageProgressBarPreferenceController mStorageUsageProgressBarController;
private VolumeOptionMenuController mOptionMenuController;
private final StorageEventListener mStorageEventListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo volumeInfo, int oldState, int newState) {
if (!StorageUtils.isStorageSettingsInterestedVolume(volumeInfo)) {
return;
}
final StorageEntry changedStorageEntry = new StorageEntry(getContext(), volumeInfo);
switch (volumeInfo.getState()) {
case VolumeInfo.STATE_MOUNTED:
case VolumeInfo.STATE_MOUNTED_READ_ONLY:
case VolumeInfo.STATE_UNMOUNTABLE:
// 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:
// Remove removed storage from list and don't show it on spinner.
if (mStorageEntries.remove(changedStorageEntry)) {
if (changedStorageEntry.equals(mSelectedStorageEntry)) {
mSelectedStorageEntry =
StorageEntry.getDefaultInternalStorageEntry(getContext());
}
refreshUi();
}
break;
default:
// Do nothing.
}
}
@Override
public void onVolumeRecordChanged(VolumeRecord volumeRecord) {
if (StorageUtils.isVolumeRecordMissed(mStorageManager, 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();
}
}
}
@Override
public void onVolumeForgotten(String fsUuid) {
final StorageEntry storageEntry = new StorageEntry(
new VolumeRecord(VolumeInfo.TYPE_PUBLIC, fsUuid));
if (mStorageEntries.remove(storageEntry)) {
if (mSelectedStorageEntry.equals(storageEntry)) {
mSelectedStorageEntry =
StorageEntry.getDefaultInternalStorageEntry(getContext());
}
refreshUi();
}
}
@Override
public void onDiskScanned(DiskInfo disk, int volumeCount) {
if (!StorageUtils.isDiskUnsupported(disk)) {
return;
}
final StorageEntry storageEntry = new StorageEntry(disk);
if (!mStorageEntries.contains(storageEntry)) {
mStorageEntries.add(storageEntry);
refreshUi();
}
}
@Override
public void onDiskDestroyed(DiskInfo disk) {
final StorageEntry storageEntry = new StorageEntry(disk);
if (mStorageEntries.remove(storageEntry)) {
if (mSelectedStorageEntry.equals(storageEntry)) {
mSelectedStorageEntry =
StorageEntry.getDefaultInternalStorageEntry(getContext());
}
refreshUi();
}
}
};
@Override
public Fragment[] getFragments() {
if (mFragments != null) {
return mFragments;
}
final Bundle workBundle = new Bundle();
workBundle.putInt(EXTRA_PROFILE, ProfileType.WORK);
final Fragment workFragment = new StorageDashboardFragment();
final Fragment workFragment = new StorageCategoryFragment();
workFragment.setArguments(workBundle);
final Bundle personalBundle = new Bundle();
personalBundle.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
final Fragment personalFragment = new StorageDashboardFragment();
final Fragment personalFragment = new StorageCategoryFragment();
personalFragment.setArguments(personalBundle);
return new Fragment[] {
mFragments = new Fragment[] {
personalFragment,
workFragment
};
return mFragments;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.storage_dashboard_header_fragment;
}
private void refreshUi() {
mStorageSelectionController.setStorageEntries(mStorageEntries);
mStorageSelectionController.setSelectedStorageEntry(mSelectedStorageEntry);
mStorageUsageProgressBarController.setSelectedStorageEntry(mSelectedStorageEntry);
for (Fragment fragment : getFragments()) {
if (!(fragment instanceof StorageCategoryFragment)) {
throw new IllegalStateException("Wrong fragment type to refreshUi");
}
((StorageCategoryFragment) fragment).refreshUi(mSelectedStorageEntry);
}
mOptionMenuController.setSelectedStorageEntry(mSelectedStorageEntry);
getActivity().invalidateOptionsMenu();
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Activity activity = getActivity();
mStorageManager = activity.getSystemService(StorageManager.class);
if (icicle == null) {
final VolumeInfo specifiedVolumeInfo =
Utils.maybeInitializeVolume(mStorageManager, getArguments());
mSelectedStorageEntry = specifiedVolumeInfo == null
? StorageEntry.getDefaultInternalStorageEntry(getContext())
: new StorageEntry(getContext(), specifiedVolumeInfo);
} else {
mSelectedStorageEntry = icicle.getParcelable(SELECTED_STORAGE_ENTRY_KEY);
}
initializeOptionsMenu(activity);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
use(AutomaticStorageManagementSwitchPreferenceController.class).setFragmentManager(
getFragmentManager());
mStorageSelectionController = use(StorageSelectionPreferenceController.class);
mStorageSelectionController.setOnItemSelectedListener(storageEntry -> {
mSelectedStorageEntry = storageEntry;
refreshUi();
if (storageEntry.isDiskInfoUnsupported() || storageEntry.isUnmountable()) {
DiskInitFragment.show(this, R.string.storage_dialog_unmountable,
storageEntry.getDiskId());
} else if (storageEntry.isVolumeRecordMissed()) {
StorageUtils.launchForgetMissingVolumeRecordFragment(getContext(), storageEntry);
}
});
mStorageUsageProgressBarController = use(StorageUsageProgressBarPreferenceController.class);
}
@VisibleForTesting
void initializeOptionsMenu(Activity activity) {
mOptionMenuController = new VolumeOptionMenuController(activity, this,
mSelectedStorageEntry);
getSettingsLifecycle().addObserver(mOptionMenuController);
setHasOptionsMenu(true);
activity.invalidateOptionsMenu();
}
@Override
public void onResume() {
super.onResume();
mStorageEntries.clear();
mStorageEntries.addAll(StorageUtils.getAllStorageEntries(getContext(), mStorageManager));
refreshUi();
mStorageManager.registerListener(mStorageEventListener);
}
@Override
public void onPause() {
super.onPause();
mStorageManager.unregisterListener(mStorageEventListener);
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putParcelable(SELECTED_STORAGE_ENTRY_KEY, mSelectedStorageEntry);
super.onSaveInstanceState(outState);
}
@Override
public int getHelpResource() {
return R.string.help_url_storage_dashboard;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_STORAGE_CATEGORY;
}
@Override
protected String getLogTag() {
return TAG;
}
}