Storage Settings shift profile selection tab position am: c0b5ba5225
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/14441414 Change-Id: Icd44f180bf49677adceae632ead24611fed95ffa
This commit is contained in:
80
res/xml/storage_category_fragment.xml
Normal file
80
res/xml/storage_category_fragment.xml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Layout for the storage breakdown for a profile of the primary user. -->
|
||||||
|
<PreferenceScreen
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:title="@string/storage_settings">
|
||||||
|
<Preference
|
||||||
|
android:key="free_up_space"
|
||||||
|
android:order="4"
|
||||||
|
android:title="@string/storage_free_up_space_title"
|
||||||
|
android:summary="@string/storage_free_up_space_summary"
|
||||||
|
android:icon="@drawable/ic_files_go_round"
|
||||||
|
settings:allowDividerAbove="true"/>
|
||||||
|
<!-- Preference order 100~200 are 'ONLY' for storage category preferences below. -->
|
||||||
|
<Preference
|
||||||
|
android:key="pref_public_storage"
|
||||||
|
android:title="@string/storage_files"
|
||||||
|
android:icon="@drawable/ic_folder_vd_theme_24"
|
||||||
|
android:order="100"/>
|
||||||
|
<com.android.settings.deviceinfo.StorageItemPreference
|
||||||
|
android:key="pref_images"
|
||||||
|
android:title="@string/storage_images"
|
||||||
|
android:icon="@drawable/ic_photo_library"
|
||||||
|
android:order="101"/>
|
||||||
|
<com.android.settings.deviceinfo.StorageItemPreference
|
||||||
|
android:key="pref_videos"
|
||||||
|
android:title="@string/storage_videos"
|
||||||
|
android:icon="@drawable/ic_local_movies"
|
||||||
|
android:order="102"/>
|
||||||
|
<com.android.settings.deviceinfo.StorageItemPreference
|
||||||
|
android:key="pref_audios"
|
||||||
|
android:title="@string/storage_audios"
|
||||||
|
android:icon="@drawable/ic_media_stream"
|
||||||
|
android:order="103"/>
|
||||||
|
<com.android.settings.deviceinfo.StorageItemPreference
|
||||||
|
android:key="pref_apps"
|
||||||
|
android:title="@string/storage_apps"
|
||||||
|
android:icon="@drawable/ic_storage_apps"
|
||||||
|
android:order="104"/>
|
||||||
|
<com.android.settings.deviceinfo.StorageItemPreference
|
||||||
|
android:key="pref_games"
|
||||||
|
android:title="@string/storage_games"
|
||||||
|
android:icon="@drawable/ic_videogame_vd_theme_24"
|
||||||
|
android:order="105"/>
|
||||||
|
<com.android.settings.deviceinfo.StorageItemPreference
|
||||||
|
android:key="pref_documents_and_other"
|
||||||
|
android:title="@string/storage_documents_and_other"
|
||||||
|
android:icon="@drawable/ic_folder_vd_theme_24"
|
||||||
|
android:order="106"/>
|
||||||
|
<com.android.settings.deviceinfo.StorageItemPreference
|
||||||
|
android:key="pref_system"
|
||||||
|
android:title="@string/storage_system"
|
||||||
|
android:icon="@drawable/ic_system_update"
|
||||||
|
android:order="107"/>
|
||||||
|
<com.android.settings.deviceinfo.StorageItemPreference
|
||||||
|
android:key="pref_trash"
|
||||||
|
android:title="@string/storage_trash"
|
||||||
|
android:icon="@drawable/ic_trash_can"
|
||||||
|
android:order="108"/>
|
||||||
|
<!-- Preference order 100~200 are 'ONLY' for storage category preferences above. -->
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="pref_secondary_users"
|
||||||
|
android:title="@string/storage_other_users"
|
||||||
|
android:order="201" />
|
||||||
|
</PreferenceScreen>
|
@@ -18,7 +18,6 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
android:title="@string/storage_settings"
|
android:title="@string/storage_settings"
|
||||||
android:orderingFromXml="false"
|
|
||||||
settings:keywords="@string/keywords_storage">
|
settings:keywords="@string/keywords_storage">
|
||||||
<com.android.settingslib.widget.SettingsSpinnerPreference
|
<com.android.settingslib.widget.SettingsSpinnerPreference
|
||||||
android:key="storage_spinner"
|
android:key="storage_spinner"
|
||||||
@@ -45,8 +44,8 @@
|
|||||||
android:order="4"
|
android:order="4"
|
||||||
android:title="@string/storage_free_up_space_title"
|
android:title="@string/storage_free_up_space_title"
|
||||||
android:summary="@string/storage_free_up_space_summary"
|
android:summary="@string/storage_free_up_space_summary"
|
||||||
settings:allowDividerAbove="true"
|
android:icon="@drawable/ic_files_go_round"
|
||||||
android:icon="@drawable/ic_files_go_round"/>
|
settings:allowDividerAbove="true"/>
|
||||||
<!-- Preference order 100~200 are 'ONLY' for storage category preferences below. -->
|
<!-- Preference order 100~200 are 'ONLY' for storage category preferences below. -->
|
||||||
<Preference
|
<Preference
|
||||||
android:key="pref_public_storage"
|
android:key="pref_public_storage"
|
||||||
|
42
res/xml/storage_dashboard_header_fragment.xml
Normal file
42
res/xml/storage_dashboard_header_fragment.xml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<PreferenceScreen
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:key="storage_header"
|
||||||
|
android:title="@string/storage_settings">
|
||||||
|
<com.android.settingslib.widget.SettingsSpinnerPreference
|
||||||
|
android:key="storage_spinner"
|
||||||
|
android:order="1"
|
||||||
|
settings:searchable="false"
|
||||||
|
settings:controller="com.android.settings.deviceinfo.storage.StorageSelectionPreferenceController"/>
|
||||||
|
<com.android.settingslib.widget.UsageProgressBarPreference
|
||||||
|
android:key="storage_summary"
|
||||||
|
android:order="2"
|
||||||
|
android:selectable="false"
|
||||||
|
settings:searchable="false"
|
||||||
|
settings:controller="com.android.settings.deviceinfo.storage.StorageUsageProgressBarPreferenceController"
|
||||||
|
settings:allowDividerBelow="true"/>
|
||||||
|
<com.android.settings.widget.PrimarySwitchPreference
|
||||||
|
android:fragment="com.android.settings.deletionhelper.AutomaticStorageManagerSettings"
|
||||||
|
android:key="toggle_asm"
|
||||||
|
android:title="@string/automatic_storage_manager_preference_title"
|
||||||
|
android:icon="@drawable/ic_storage"
|
||||||
|
android:order="3"
|
||||||
|
settings:allowDividerAbove="true"
|
||||||
|
settings:controller="com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController"/>
|
||||||
|
</PreferenceScreen>
|
@@ -1,52 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- Layout for the storage breakdown for a profile of the primary user. -->
|
|
||||||
<PreferenceScreen
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:title="@string/storage_settings">
|
|
||||||
<com.android.settings.deviceinfo.StorageItemPreference
|
|
||||||
android:key="pref_photos_videos"
|
|
||||||
android:title="@string/storage_photos_videos"
|
|
||||||
android:icon="@drawable/ic_photo_library"
|
|
||||||
android:order="2" />
|
|
||||||
<com.android.settings.deviceinfo.StorageItemPreference
|
|
||||||
android:key="pref_music_audio"
|
|
||||||
android:title="@string/storage_music_audio"
|
|
||||||
android:icon="@drawable/ic_media_stream"
|
|
||||||
android:order="3" />
|
|
||||||
<com.android.settings.deviceinfo.StorageItemPreference
|
|
||||||
android:key="pref_games"
|
|
||||||
android:title="@string/storage_games"
|
|
||||||
android:icon="@drawable/ic_videogame_vd_theme_24"
|
|
||||||
android:order="4" />
|
|
||||||
<com.android.settings.deviceinfo.StorageItemPreference
|
|
||||||
android:key="pref_movies"
|
|
||||||
android:title="@string/storage_movies_tv"
|
|
||||||
android:icon="@drawable/ic_local_movies"
|
|
||||||
android:order="5"
|
|
||||||
/>
|
|
||||||
<com.android.settings.deviceinfo.StorageItemPreference
|
|
||||||
android:key="pref_other_apps"
|
|
||||||
android:title="@string/storage_other_apps"
|
|
||||||
android:icon="@drawable/ic_storage_apps"
|
|
||||||
android:order="6" />
|
|
||||||
<com.android.settings.deviceinfo.StorageItemPreference
|
|
||||||
android:key="pref_files"
|
|
||||||
android:title="@string/storage_files"
|
|
||||||
android:icon="@drawable/ic_folder_vd_theme_24"
|
|
||||||
android:order="7" />
|
|
||||||
</PreferenceScreen>
|
|
@@ -16,32 +16,301 @@
|
|||||||
|
|
||||||
package com.android.settings.dashboard.profileselector;
|
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.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 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 {
|
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
|
@Override
|
||||||
public Fragment[] getFragments() {
|
public Fragment[] getFragments() {
|
||||||
|
if (mFragments != null) {
|
||||||
|
return mFragments;
|
||||||
|
}
|
||||||
|
|
||||||
final Bundle workBundle = new Bundle();
|
final Bundle workBundle = new Bundle();
|
||||||
workBundle.putInt(EXTRA_PROFILE, ProfileType.WORK);
|
workBundle.putInt(EXTRA_PROFILE, ProfileType.WORK);
|
||||||
final Fragment workFragment = new StorageDashboardFragment();
|
final Fragment workFragment = new StorageCategoryFragment();
|
||||||
workFragment.setArguments(workBundle);
|
workFragment.setArguments(workBundle);
|
||||||
|
|
||||||
final Bundle personalBundle = new Bundle();
|
final Bundle personalBundle = new Bundle();
|
||||||
personalBundle.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
|
personalBundle.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
|
||||||
final Fragment personalFragment = new StorageDashboardFragment();
|
final Fragment personalFragment = new StorageCategoryFragment();
|
||||||
personalFragment.setArguments(personalBundle);
|
personalFragment.setArguments(personalBundle);
|
||||||
|
|
||||||
return new Fragment[] {
|
mFragments = new Fragment[] {
|
||||||
personalFragment,
|
personalFragment,
|
||||||
workFragment
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
415
src/com/android/settings/deviceinfo/StorageCategoryFragment.java
Normal file
415
src/com/android/settings/deviceinfo/StorageCategoryFragment.java
Normal file
@@ -0,0 +1,415 @@
|
|||||||
|
/*
|
||||||
|
* 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.settings.SettingsEnums;
|
||||||
|
import android.app.usage.StorageStatsManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.os.UserManager;
|
||||||
|
import android.os.storage.StorageManager;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
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.dashboard.DashboardFragment;
|
||||||
|
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
||||||
|
import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
|
||||||
|
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.UserIconLoader;
|
||||||
|
import com.android.settings.deviceinfo.storage.VolumeSizesLoader;
|
||||||
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
import com.android.settings.widget.EntityHeaderController;
|
||||||
|
import com.android.settingslib.applications.StorageStatsSource;
|
||||||
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
|
||||||
|
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 StorageCategoryFragment extends DashboardFragment
|
||||||
|
implements
|
||||||
|
LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>>,
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
private static final String TAG = "StorageCategoryFrag";
|
||||||
|
private static final String SUMMARY_PREF_KEY = "storage_summary";
|
||||||
|
private static final String FREE_UP_SPACE_PREF_KEY = "free_up_space";
|
||||||
|
private static final int STORAGE_JOB_ID = 0;
|
||||||
|
private static final int ICON_JOB_ID = 1;
|
||||||
|
private static final int VOLUME_SIZE_JOB_ID = 2;
|
||||||
|
|
||||||
|
private StorageManager mStorageManager;
|
||||||
|
private UserManager mUserManager;
|
||||||
|
private StorageEntry mSelectedStorageEntry;
|
||||||
|
private PrivateStorageInfo mStorageInfo;
|
||||||
|
private SparseArray<StorageAsyncLoader.AppsStorageResult> mAppsResult;
|
||||||
|
private CachedStorageValuesHelper mCachedStorageValuesHelper;
|
||||||
|
|
||||||
|
private StorageItemPreferenceController mPreferenceController;
|
||||||
|
private List<AbstractPreferenceController> mSecondaryUsers;
|
||||||
|
private boolean mIsWorkProfile;
|
||||||
|
private int mUserId;
|
||||||
|
private Preference mFreeUpSpacePreference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh UI for specified storageEntry.
|
||||||
|
*/
|
||||||
|
public void refreshUi(StorageEntry storageEntry) {
|
||||||
|
mSelectedStorageEntry = storageEntry;
|
||||||
|
if (mPreferenceController == null) {
|
||||||
|
// Check null here because when onResume, StorageCategoryFragment may not
|
||||||
|
// onAttach to createPreferenceControllers and mPreferenceController will be null.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
|
||||||
|
|
||||||
|
if (!mSelectedStorageEntry.isMounted()) {
|
||||||
|
// Set null volume to hide category stats.
|
||||||
|
mPreferenceController.setVolume(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mSelectedStorageEntry.isPrivate()) {
|
||||||
|
// Stats data is only available on private volumes.
|
||||||
|
getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
|
||||||
|
getLoaderManager()
|
||||||
|
.restartLoader(VOLUME_SIZE_JOB_ID, Bundle.EMPTY, new VolumeSizeCallbacks());
|
||||||
|
getLoaderManager().restartLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
|
||||||
|
} else {
|
||||||
|
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle icicle) {
|
||||||
|
super.onCreate(icicle);
|
||||||
|
|
||||||
|
mStorageManager = getActivity().getSystemService(StorageManager.class);
|
||||||
|
|
||||||
|
initializePreference();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializePreference() {
|
||||||
|
mFreeUpSpacePreference = getPreferenceScreen().findPreference(FREE_UP_SPACE_PREF_KEY);
|
||||||
|
mFreeUpSpacePreference.setOnPreferenceClickListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
// These member variables are initialized befoer super.onAttach for
|
||||||
|
// createPreferenceControllers to work correctly.
|
||||||
|
mUserManager = context.getSystemService(UserManager.class);
|
||||||
|
mIsWorkProfile = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE)
|
||||||
|
== ProfileSelectFragment.ProfileType.WORK;
|
||||||
|
mUserId = Utils.getCurrentUserId(mUserManager, mIsWorkProfile);
|
||||||
|
|
||||||
|
super.onAttach(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View v, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(v, savedInstanceState);
|
||||||
|
initializeCacheProvider();
|
||||||
|
maybeSetLoading(isQuotaSupported());
|
||||||
|
|
||||||
|
EntityHeaderController.newInstance(getActivity(), this /*fragment*/,
|
||||||
|
null /* header view */)
|
||||||
|
.setRecyclerView(getListView(), getSettingsLifecycle());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
if (mSelectedStorageEntry != null) {
|
||||||
|
refreshUi(mSelectedStorageEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onReceivedSizes() {
|
||||||
|
boolean stopLoading = false;
|
||||||
|
if (mStorageInfo != null) {
|
||||||
|
final long privateUsedBytes = mStorageInfo.totalBytes - mStorageInfo.freeBytes;
|
||||||
|
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
|
||||||
|
mPreferenceController.setUsedSize(privateUsedBytes);
|
||||||
|
mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
|
||||||
|
for (int i = 0, size = mSecondaryUsers.size(); i < size; i++) {
|
||||||
|
final AbstractPreferenceController controller = mSecondaryUsers.get(i);
|
||||||
|
if (controller instanceof SecondaryUserController) {
|
||||||
|
SecondaryUserController userController = (SecondaryUserController) controller;
|
||||||
|
userController.setTotalSize(mStorageInfo.totalBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stopLoading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mAppsResult != null) {
|
||||||
|
mPreferenceController.onLoadFinished(mAppsResult, mUserId);
|
||||||
|
updateSecondaryUserControllers(mSecondaryUsers, mAppsResult);
|
||||||
|
stopLoading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setLoading always causes a flicker, so let's avoid doing it.
|
||||||
|
if (stopLoading) {
|
||||||
|
if (getView().findViewById(R.id.loading_container).getVisibility() == View.VISIBLE) {
|
||||||
|
setLoading(false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMetricsCategory() {
|
||||||
|
return SettingsEnums.SETTINGS_STORAGE_CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getLogTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getPreferenceScreenResId() {
|
||||||
|
return R.xml.storage_category_fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||||
|
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||||
|
final StorageManager sm = context.getSystemService(StorageManager.class);
|
||||||
|
mPreferenceController = new StorageItemPreferenceController(context, this,
|
||||||
|
null /* volume */, new StorageManagerVolumeProvider(sm), mIsWorkProfile);
|
||||||
|
controllers.add(mPreferenceController);
|
||||||
|
|
||||||
|
mSecondaryUsers = SecondaryUserController.getSecondaryUserControllers(context,
|
||||||
|
mUserManager, mIsWorkProfile /* isWorkProfileOnly */);
|
||||||
|
controllers.addAll(mSecondaryUsers);
|
||||||
|
|
||||||
|
return controllers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the secondary user controller sizes.
|
||||||
|
*/
|
||||||
|
private void updateSecondaryUserControllers(List<AbstractPreferenceController> controllers,
|
||||||
|
SparseArray<StorageAsyncLoader.AppsStorageResult> stats) {
|
||||||
|
for (int i = 0, size = controllers.size(); i < size; i++) {
|
||||||
|
final AbstractPreferenceController controller = controllers.get(i);
|
||||||
|
if (controller instanceof StorageAsyncLoader.ResultHandler) {
|
||||||
|
StorageAsyncLoader.ResultHandler userController =
|
||||||
|
(StorageAsyncLoader.ResultHandler) controller;
|
||||||
|
userController.handleResult(stats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> onCreateLoader(int id,
|
||||||
|
Bundle args) {
|
||||||
|
final Context context = getContext();
|
||||||
|
return new StorageAsyncLoader(context, mUserManager,
|
||||||
|
mSelectedStorageEntry.getFsUuid(),
|
||||||
|
new StorageStatsSource(context),
|
||||||
|
context.getPackageManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFinished(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader,
|
||||||
|
SparseArray<StorageAsyncLoader.AppsStorageResult> data) {
|
||||||
|
mAppsResult = data;
|
||||||
|
maybeCacheFreshValues();
|
||||||
|
onReceivedSizes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
if (preference == mFreeUpSpacePreference) {
|
||||||
|
final Context context = getContext();
|
||||||
|
final MetricsFeatureProvider metricsFeatureProvider =
|
||||||
|
FeatureFactory.getFactory(context).getMetricsFeatureProvider();
|
||||||
|
metricsFeatureProvider.logClickedPreference(preference, getMetricsCategory());
|
||||||
|
metricsFeatureProvider.action(context, SettingsEnums.STORAGE_FREE_UP_SPACE_NOW);
|
||||||
|
final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
|
||||||
|
context.startActivityAsUser(intent, new UserHandle(mUserId));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setCachedStorageValuesHelper(CachedStorageValuesHelper helper) {
|
||||||
|
mCachedStorageValuesHelper = helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public PrivateStorageInfo getPrivateStorageInfo() {
|
||||||
|
return mStorageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setPrivateStorageInfo(PrivateStorageInfo info) {
|
||||||
|
mStorageInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public SparseArray<StorageAsyncLoader.AppsStorageResult> getAppsStorageResult() {
|
||||||
|
return mAppsResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setAppsStorageResult(SparseArray<StorageAsyncLoader.AppsStorageResult> info) {
|
||||||
|
mAppsResult = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void initializeCachedValues() {
|
||||||
|
final PrivateStorageInfo info = mCachedStorageValuesHelper.getCachedPrivateStorageInfo();
|
||||||
|
final SparseArray<StorageAsyncLoader.AppsStorageResult> loaderResult =
|
||||||
|
mCachedStorageValuesHelper.getCachedAppsStorageResult();
|
||||||
|
if (info == null || loaderResult == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mStorageInfo = info;
|
||||||
|
mAppsResult = loaderResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate loading UI and animation if it's necessary.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public void maybeSetLoading(boolean isQuotaSupported) {
|
||||||
|
// If we have fast stats, we load until both have loaded.
|
||||||
|
// If we have slow stats, we load when we get the total volume sizes.
|
||||||
|
if ((isQuotaSupported && (mStorageInfo == null || mAppsResult == null))
|
||||||
|
|| (!isQuotaSupported && mStorageInfo == null)) {
|
||||||
|
setLoading(true /* loading */, false /* animate */);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeCacheProvider() {
|
||||||
|
mCachedStorageValuesHelper = new CachedStorageValuesHelper(getContext(), mUserId);
|
||||||
|
initializeCachedValues();
|
||||||
|
onReceivedSizes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeCacheFreshValues() {
|
||||||
|
if (mStorageInfo != null && mAppsResult != null) {
|
||||||
|
mCachedStorageValuesHelper.cacheResult(mStorageInfo, mAppsResult.get(mUserId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isQuotaSupported() {
|
||||||
|
return mSelectedStorageEntry.isMounted()
|
||||||
|
&& getActivity().getSystemService(StorageStatsManager.class)
|
||||||
|
.isQuotaSupported(mSelectedStorageEntry.getFsUuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IconLoaderCallbacks exists because StorageCategoryFragment already implements
|
||||||
|
* LoaderCallbacks for a different type.
|
||||||
|
*/
|
||||||
|
public final class IconLoaderCallbacks
|
||||||
|
implements LoaderManager.LoaderCallbacks<SparseArray<Drawable>> {
|
||||||
|
@Override
|
||||||
|
public Loader<SparseArray<Drawable>> onCreateLoader(int id, Bundle args) {
|
||||||
|
return new UserIconLoader(
|
||||||
|
getContext(),
|
||||||
|
() -> UserIconLoader.loadUserIconsWithContext(getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFinished(
|
||||||
|
Loader<SparseArray<Drawable>> loader, SparseArray<Drawable> data) {
|
||||||
|
mSecondaryUsers
|
||||||
|
.stream()
|
||||||
|
.filter(controller -> controller instanceof UserIconLoader.UserIconHandler)
|
||||||
|
.forEach(
|
||||||
|
controller ->
|
||||||
|
((UserIconLoader.UserIconHandler) controller)
|
||||||
|
.handleUserIcons(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(Loader<SparseArray<Drawable>> loader) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VolumeSizeCallbacks exists because StorageCategoryFragment already implements
|
||||||
|
* LoaderCallbacks for a different type.
|
||||||
|
*/
|
||||||
|
public final class VolumeSizeCallbacks
|
||||||
|
implements LoaderManager.LoaderCallbacks<PrivateStorageInfo> {
|
||||||
|
@Override
|
||||||
|
public Loader<PrivateStorageInfo> onCreateLoader(int id, Bundle args) {
|
||||||
|
final Context context = getContext();
|
||||||
|
final StorageManagerVolumeProvider smvp =
|
||||||
|
new StorageManagerVolumeProvider(mStorageManager);
|
||||||
|
final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class);
|
||||||
|
return new VolumeSizesLoader(context, smvp, stats,
|
||||||
|
mSelectedStorageEntry.getVolumeInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(Loader<PrivateStorageInfo> loader) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFinished(
|
||||||
|
Loader<PrivateStorageInfo> loader, PrivateStorageInfo privateStorageInfo) {
|
||||||
|
if (privateStorageInfo == null) {
|
||||||
|
getActivity().finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mStorageInfo = privateStorageInfo;
|
||||||
|
maybeCacheFreshValues();
|
||||||
|
onReceivedSizes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -43,7 +43,6 @@ import androidx.preference.Preference;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
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.AutomaticStorageManagementSwitchPreferenceController;
|
||||||
import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
|
import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
|
||||||
import com.android.settings.deviceinfo.storage.DiskInitFragment;
|
import com.android.settings.deviceinfo.storage.DiskInitFragment;
|
||||||
@@ -69,8 +68,20 @@ import com.android.settingslib.search.SearchIndexable;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
@SearchIndexable
|
@SearchIndexable
|
||||||
public class StorageDashboardFragment extends DashboardFragment
|
public class StorageDashboardFragment extends DashboardFragment
|
||||||
implements
|
implements
|
||||||
@@ -104,7 +115,7 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
private final StorageEventListener mStorageEventListener = new StorageEventListener() {
|
private final StorageEventListener mStorageEventListener = new StorageEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onVolumeStateChanged(VolumeInfo volumeInfo, int oldState, int newState) {
|
public void onVolumeStateChanged(VolumeInfo volumeInfo, int oldState, int newState) {
|
||||||
if (!isInteresting(volumeInfo)) {
|
if (!StorageUtils.isStorageSettingsInterestedVolume(volumeInfo)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +157,7 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onVolumeRecordChanged(VolumeRecord volumeRecord) {
|
public void onVolumeRecordChanged(VolumeRecord volumeRecord) {
|
||||||
if (isVolumeRecordMissed(volumeRecord)) {
|
if (StorageUtils.isVolumeRecordMissed(mStorageManager, volumeRecord)) {
|
||||||
// VolumeRecord is a metadata of VolumeInfo, if a VolumeInfo is missing
|
// VolumeRecord is a metadata of VolumeInfo, if a VolumeInfo is missing
|
||||||
// (e.g., internal SD card is removed.) show the missing storage to users,
|
// (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.
|
// users can insert the SD card or manually forget the storage from the device.
|
||||||
@@ -159,7 +170,7 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
// Find mapped VolumeInfo and replace with existing one for something changed.
|
// Find mapped VolumeInfo and replace with existing one for something changed.
|
||||||
// (e.g., Renamed.)
|
// (e.g., Renamed.)
|
||||||
final VolumeInfo mappedVolumeInfo =
|
final VolumeInfo mappedVolumeInfo =
|
||||||
mStorageManager.findVolumeByUuid(volumeRecord.getFsUuid());
|
mStorageManager.findVolumeByUuid(volumeRecord.getFsUuid());
|
||||||
if (mappedVolumeInfo == null) {
|
if (mappedVolumeInfo == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -190,7 +201,7 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDiskScanned(DiskInfo disk, int volumeCount) {
|
public void onDiskScanned(DiskInfo disk, int volumeCount) {
|
||||||
if (!isDiskUnsupported(disk)) {
|
if (!StorageUtils.isDiskUnsupported(disk)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final StorageEntry storageEntry = new StorageEntry(disk);
|
final StorageEntry storageEntry = new StorageEntry(disk);
|
||||||
@@ -213,33 +224,6 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static boolean isInteresting(VolumeInfo volumeInfo) {
|
|
||||||
switch (volumeInfo.getType()) {
|
|
||||||
case VolumeInfo.TYPE_PRIVATE:
|
|
||||||
case VolumeInfo.TYPE_PUBLIC:
|
|
||||||
case VolumeInfo.TYPE_STUB:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshUi() {
|
private void refreshUi() {
|
||||||
mStorageSelectionController.setStorageEntries(mStorageEntries);
|
mStorageSelectionController.setStorageEntries(mStorageEntries);
|
||||||
mStorageSelectionController.setSelectedStorageEntry(mSelectedStorageEntry);
|
mStorageSelectionController.setSelectedStorageEntry(mSelectedStorageEntry);
|
||||||
@@ -297,9 +281,8 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
// These member variables are initialized befoer super.onAttach for
|
// These member variables are initialized befoer super.onAttach for
|
||||||
// createPreferenceControllers to work correctly.
|
// createPreferenceControllers to work correctly.
|
||||||
mUserManager = context.getSystemService(UserManager.class);
|
mUserManager = context.getSystemService(UserManager.class);
|
||||||
mIsWorkProfile = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE)
|
mIsWorkProfile = false;
|
||||||
== ProfileSelectFragment.ProfileType.WORK;
|
mUserId = UserHandle.myUserId();
|
||||||
mUserId = Utils.getCurrentUserId(mUserManager, mIsWorkProfile);
|
|
||||||
|
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
use(AutomaticStorageManagementSwitchPreferenceController.class).setFragmentManager(
|
use(AutomaticStorageManagementSwitchPreferenceController.class).setFragmentManager(
|
||||||
@@ -334,8 +317,7 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
initializeCacheProvider();
|
initializeCacheProvider();
|
||||||
maybeSetLoading(isQuotaSupported());
|
maybeSetLoading(isQuotaSupported());
|
||||||
|
|
||||||
final Activity activity = getActivity();
|
EntityHeaderController.newInstance(getActivity(), this /*fragment*/,
|
||||||
EntityHeaderController.newInstance(activity, this /*fragment*/,
|
|
||||||
null /* header view */)
|
null /* header view */)
|
||||||
.setRecyclerView(getListView(), getSettingsLifecycle());
|
.setRecyclerView(getListView(), getSettingsLifecycle());
|
||||||
}
|
}
|
||||||
@@ -345,18 +327,7 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
mStorageEntries.clear();
|
mStorageEntries.clear();
|
||||||
mStorageEntries.addAll(mStorageManager.getVolumes().stream()
|
mStorageEntries.addAll(StorageUtils.getAllStorageEntries(getContext(), mStorageManager));
|
||||||
.filter(volumeInfo -> isInteresting(volumeInfo))
|
|
||||||
.map(volumeInfo -> new StorageEntry(getContext(), volumeInfo))
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
mStorageEntries.addAll(mStorageManager.getDisks().stream()
|
|
||||||
.filter(disk -> isDiskUnsupported(disk))
|
|
||||||
.map(disk -> new StorageEntry(disk))
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
mStorageEntries.addAll(mStorageManager.getVolumeRecords().stream()
|
|
||||||
.filter(volumeRecord -> isVolumeRecordMissed(volumeRecord))
|
|
||||||
.map(volumeRecord -> new StorageEntry(volumeRecord))
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
refreshUi();
|
refreshUi();
|
||||||
mStorageManager.registerListener(mStorageEventListener);
|
mStorageManager.registerListener(mStorageEventListener);
|
||||||
}
|
}
|
||||||
@@ -381,19 +352,18 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
private void onReceivedSizes() {
|
private void onReceivedSizes() {
|
||||||
boolean stopLoading = false;
|
boolean stopLoading = false;
|
||||||
if (mStorageInfo != null) {
|
if (mStorageInfo != null) {
|
||||||
long privateUsedBytes = mStorageInfo.totalBytes - mStorageInfo.freeBytes;
|
final long privateUsedBytes = mStorageInfo.totalBytes - mStorageInfo.freeBytes;
|
||||||
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
|
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
|
||||||
mPreferenceController.setUsedSize(privateUsedBytes);
|
mPreferenceController.setUsedSize(privateUsedBytes);
|
||||||
mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
|
mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
|
||||||
for (int i = 0, size = mSecondaryUsers.size(); i < size; i++) {
|
for (int i = 0, size = mSecondaryUsers.size(); i < size; i++) {
|
||||||
AbstractPreferenceController controller = mSecondaryUsers.get(i);
|
final AbstractPreferenceController controller = mSecondaryUsers.get(i);
|
||||||
if (controller instanceof SecondaryUserController) {
|
if (controller instanceof SecondaryUserController) {
|
||||||
SecondaryUserController userController = (SecondaryUserController) controller;
|
SecondaryUserController userController = (SecondaryUserController) controller;
|
||||||
userController.setTotalSize(mStorageInfo.totalBytes);
|
userController.setTotalSize(mStorageInfo.totalBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stopLoading = true;
|
stopLoading = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAppsResult != null) {
|
if (mAppsResult != null) {
|
||||||
@@ -428,7 +398,7 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
@Override
|
@Override
|
||||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||||
StorageManager sm = context.getSystemService(StorageManager.class);
|
final StorageManager sm = context.getSystemService(StorageManager.class);
|
||||||
mPreferenceController = new StorageItemPreferenceController(context, this,
|
mPreferenceController = new StorageItemPreferenceController(context, this,
|
||||||
null /* volume */, new StorageManagerVolumeProvider(sm), mIsWorkProfile);
|
null /* volume */, new StorageManagerVolumeProvider(sm), mIsWorkProfile);
|
||||||
controllers.add(mPreferenceController);
|
controllers.add(mPreferenceController);
|
||||||
@@ -440,18 +410,13 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
return controllers;
|
return controllers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
protected void setVolume(VolumeInfo info) {
|
|
||||||
mSelectedStorageEntry = new StorageEntry(getContext(), info);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the secondary user controller sizes.
|
* Updates the secondary user controller sizes.
|
||||||
*/
|
*/
|
||||||
private void updateSecondaryUserControllers(List<AbstractPreferenceController> controllers,
|
private void updateSecondaryUserControllers(List<AbstractPreferenceController> controllers,
|
||||||
SparseArray<StorageAsyncLoader.AppsStorageResult> stats) {
|
SparseArray<StorageAsyncLoader.AppsStorageResult> stats) {
|
||||||
for (int i = 0, size = controllers.size(); i < size; i++) {
|
for (int i = 0, size = controllers.size(); i < size; i++) {
|
||||||
AbstractPreferenceController controller = controllers.get(i);
|
final AbstractPreferenceController controller = controllers.get(i);
|
||||||
if (controller instanceof StorageAsyncLoader.ResultHandler) {
|
if (controller instanceof StorageAsyncLoader.ResultHandler) {
|
||||||
StorageAsyncLoader.ResultHandler userController =
|
StorageAsyncLoader.ResultHandler userController =
|
||||||
(StorageAsyncLoader.ResultHandler) controller;
|
(StorageAsyncLoader.ResultHandler) controller;
|
||||||
@@ -552,9 +517,9 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void initializeCachedValues() {
|
void initializeCachedValues() {
|
||||||
PrivateStorageInfo info = mCachedStorageValuesHelper.getCachedPrivateStorageInfo();
|
final PrivateStorageInfo info = mCachedStorageValuesHelper.getCachedPrivateStorageInfo();
|
||||||
SparseArray<StorageAsyncLoader.AppsStorageResult> loaderResult =
|
final SparseArray<StorageAsyncLoader.AppsStorageResult> loaderResult =
|
||||||
mCachedStorageValuesHelper.getCachedAppsStorageResult();
|
mCachedStorageValuesHelper.getCachedAppsStorageResult();
|
||||||
if (info == null || loaderResult == null) {
|
if (info == null || loaderResult == null) {
|
||||||
return;
|
return;
|
||||||
@@ -564,12 +529,15 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
mAppsResult = loaderResult;
|
mAppsResult = loaderResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate loading UI and animation if it's necessary.
|
||||||
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void maybeSetLoading(boolean isQuotaSupported) {
|
public void maybeSetLoading(boolean isQuotaSupported) {
|
||||||
// If we have fast stats, we load until both have loaded.
|
// If we have fast stats, we load until both have loaded.
|
||||||
// If we have slow stats, we load when we get the total volume sizes.
|
// If we have slow stats, we load when we get the total volume sizes.
|
||||||
if ((isQuotaSupported && (mStorageInfo == null || mAppsResult == null)) ||
|
if ((isQuotaSupported && (mStorageInfo == null || mAppsResult == null))
|
||||||
(!isQuotaSupported && mStorageInfo == null)) {
|
|| (!isQuotaSupported && mStorageInfo == null)) {
|
||||||
setLoading(true /* loading */, false /* animate */);
|
setLoading(true /* loading */, false /* animate */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -622,6 +590,10 @@ public class StorageDashboardFragment extends DashboardFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VolumeSizeCallbacks exists because StorageCategoryFragment already implements
|
||||||
|
* LoaderCallbacks for a different type.
|
||||||
|
*/
|
||||||
public final class VolumeSizeCallbacks
|
public final class VolumeSizeCallbacks
|
||||||
implements LoaderManager.LoaderCallbacks<PrivateStorageInfo> {
|
implements LoaderManager.LoaderCallbacks<PrivateStorageInfo> {
|
||||||
@Override
|
@Override
|
||||||
|
@@ -22,6 +22,7 @@ import android.content.Context;
|
|||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.storage.DiskInfo;
|
||||||
import android.os.storage.StorageManager;
|
import android.os.storage.StorageManager;
|
||||||
import android.os.storage.VolumeInfo;
|
import android.os.storage.VolumeInfo;
|
||||||
import android.os.storage.VolumeRecord;
|
import android.os.storage.VolumeRecord;
|
||||||
@@ -36,11 +37,67 @@ import com.android.settings.core.SubSettingLauncher;
|
|||||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
import com.android.settings.deviceinfo.PrivateVolumeForget;
|
import com.android.settings.deviceinfo.PrivateVolumeForget;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/** Storage utilities */
|
/** Storage utilities */
|
||||||
public class StorageUtils {
|
public class StorageUtils {
|
||||||
|
|
||||||
private static final String TAG = "StorageUtils";
|
private static final String TAG = "StorageUtils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects and returns all kinds of StorageEntry which will show in Storage Settings.
|
||||||
|
*/
|
||||||
|
public static List<StorageEntry> getAllStorageEntries(Context context,
|
||||||
|
StorageManager storageManager) {
|
||||||
|
final List<StorageEntry> storageEntries = new ArrayList<>();
|
||||||
|
storageEntries.addAll(storageManager.getVolumes().stream()
|
||||||
|
.filter(volumeInfo -> isStorageSettingsInterestedVolume(volumeInfo))
|
||||||
|
.map(volumeInfo -> new StorageEntry(context, volumeInfo))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
storageEntries.addAll(storageManager.getDisks().stream()
|
||||||
|
.filter(disk -> isDiskUnsupported(disk))
|
||||||
|
.map(disk -> new StorageEntry(disk))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
storageEntries.addAll(storageManager.getVolumeRecords().stream()
|
||||||
|
.filter(volumeRecord -> isVolumeRecordMissed(storageManager, volumeRecord))
|
||||||
|
.map(volumeRecord -> new StorageEntry(volumeRecord))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
return storageEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the volumeInfo may be displayed in Storage Settings.
|
||||||
|
*/
|
||||||
|
public static boolean isStorageSettingsInterestedVolume(VolumeInfo volumeInfo) {
|
||||||
|
switch (volumeInfo.getType()) {
|
||||||
|
case VolumeInfo.TYPE_PRIVATE:
|
||||||
|
case VolumeInfo.TYPE_PUBLIC:
|
||||||
|
case VolumeInfo.TYPE_STUB:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VolumeRecord is a metadata of VolumeInfo, this is the case where a VolumeInfo is missing.
|
||||||
|
* (e.g., internal SD card is removed.)
|
||||||
|
*/
|
||||||
|
public static boolean isVolumeRecordMissed(StorageManager storageManager,
|
||||||
|
VolumeRecord volumeRecord) {
|
||||||
|
return volumeRecord.getType() == VolumeInfo.TYPE_PRIVATE
|
||||||
|
&& storageManager.findVolumeByUuid(volumeRecord.getFsUuid()) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unsupported disk is the disk of problem format, android is not able to mount automatically.
|
||||||
|
*/
|
||||||
|
public static boolean isDiskUnsupported(DiskInfo disk) {
|
||||||
|
return disk.volumeCount == 0 && disk.size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** Launches the fragment to forget a specified missing volume record. */
|
/** Launches the fragment to forget a specified missing volume record. */
|
||||||
public static void launchForgetMissingVolumeRecordFragment(Context context,
|
public static void launchForgetMissingVolumeRecordFragment(Context context,
|
||||||
StorageEntry storageEntry) {
|
StorageEntry storageEntry) {
|
||||||
|
@@ -37,6 +37,7 @@ com.android.settings.datausage.DataUsageSummary
|
|||||||
com.android.settings.datetime.timezone.TimeZoneSettings
|
com.android.settings.datetime.timezone.TimeZoneSettings
|
||||||
com.android.settings.development.compat.PlatformCompatDashboard
|
com.android.settings.development.compat.PlatformCompatDashboard
|
||||||
com.android.settings.deviceinfo.PublicVolumeSettings
|
com.android.settings.deviceinfo.PublicVolumeSettings
|
||||||
|
com.android.settings.deviceinfo.StorageDashboardNoHeaderFragment
|
||||||
com.android.settings.deviceinfo.legal.ModuleLicensesDashboard
|
com.android.settings.deviceinfo.legal.ModuleLicensesDashboard
|
||||||
com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionCamera
|
com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionCamera
|
||||||
com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionLocation
|
com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionLocation
|
||||||
|
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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.dashboard.profileselector;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import androidx.test.annotation.UiThreadTest;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ProfileSelectStorageFragmentTest {
|
||||||
|
|
||||||
|
private ProfileSelectStorageFragment mFragment;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
@UiThreadTest
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
|
if (Looper.myLooper() == null) {
|
||||||
|
Looper.prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
mFragment = new ProfileSelectStorageFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@UiThreadTest
|
||||||
|
public void test_initializeOptionsMenuInvalidatesExistingMenu() {
|
||||||
|
final Activity activity = mock(Activity.class);
|
||||||
|
|
||||||
|
mFragment.initializeOptionsMenu(activity);
|
||||||
|
|
||||||
|
verify(activity).invalidateOptionsMenu();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user