Add support for user profiles to the Storage Settings.

This adds new preferences for each profile (such as the work
profile) and defines a new view for viewing the storage
breakdown for the individual profile. The functionality closely
mimics the presentation on the main view, but without the system-wide
breakdown and without any additional users/profiles.

Bug: 34715777
Test: Settings Robotests

Change-Id: I19d449b648c6566331fd02e45c2e45f8c74ea7e7
This commit is contained in:
Daniel Nishi
2017-02-15 15:25:48 -08:00
parent 182e6ce2c7
commit 9f60f42a94
15 changed files with 553 additions and 62 deletions

View File

@@ -16,40 +16,50 @@
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/storage_settings"> android:title="@string/storage_settings"
android:orderingFromXml="false">
<com.android.settings.deviceinfo.storage.StorageSummaryDonutPreference <com.android.settings.deviceinfo.storage.StorageSummaryDonutPreference
android:key="pref_summary" /> android:key="pref_summary"
android:order="0" />
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate <com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
android:key="pref_photos_videos" android:key="pref_photos_videos"
android:title="@string/storage_photos_videos"> android:title="@string/storage_photos_videos"
android:order="1" >
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate> </com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate <com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
android:key="pref_music_audio" android:key="pref_music_audio"
android:title="@string/storage_music_audio"> android:title="@string/storage_music_audio"
android:order="2" >
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate> </com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate <com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
android:key="pref_games" android:key="pref_games"
android:title="@string/storage_games"> android:title="@string/storage_games"
android:order="3" >
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate> </com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate <com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
android:key="pref_other_apps" android:key="pref_other_apps"
android:title="@string/storage_other_apps"> android:title="@string/storage_other_apps"
android:order="4" >
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate> </com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate <com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
android:key="pref_files" android:key="pref_files"
android:title="@string/storage_files"> android:title="@string/storage_files"
android:order="5" >
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate> </com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate <com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
android:key="pref_system" android:key="pref_system"
android:title="@string/storage_detail_system"> android:title="@string/storage_detail_system"
android:order="100" >
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate> </com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
<PreferenceCategory <PreferenceCategory
android:key="pref_secondary_users" android:key="pref_secondary_users"
android:title="@string/storage_other_users" /> android:title="@string/storage_other_users"
android:order="200" />
<Preference <Preference
android:key="manage_storage" android:key="manage_storage"
android:title="@string/storage_menu_manage" android:title="@string/storage_menu_manage"
android:icon="@drawable/ic_settings_storage" android:icon="@drawable/ic_settings_storage"
android:fragment="com.android.settings.deletionhelper.AutomaticStorageManagerSettings"> android:fragment="com.android.settings.deletionhelper.AutomaticStorageManagerSettings"
android:order="300" >
</Preference> </Preference>
</PreferenceScreen> </PreferenceScreen>

View File

@@ -0,0 +1,41 @@
<?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.storage.StorageItemPreferenceAlternate
android:key="pref_photos_videos"
android:title="@string/storage_photos_videos">
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
android:key="pref_music_audio"
android:title="@string/storage_music_audio">
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
android:key="pref_games"
android:title="@string/storage_games">
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
android:key="pref_other_apps"
android:title="@string/storage_other_apps">
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
android:key="pref_files"
android:title="@string/storage_files">
</com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate>
</PreferenceScreen>

View File

@@ -65,6 +65,7 @@ import android.os.ServiceManager;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.os.storage.StorageManager; import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.preference.PreferenceFrameLayout; import android.preference.PreferenceFrameLayout;
import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Contacts;
@@ -1258,4 +1259,21 @@ public final class Utils extends com.android.settingslib.Utils {
(user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
&& user.profileGroupId == profile.profileGroupId); && user.profileGroupId == profile.profileGroupId);
} }
/**
* Tries to initalize a volume with the given bundle. If it is a valid, private, and readable
* {@link VolumeInfo}, it is returned. If it is not valid, null is returned.
*/
@Nullable
public static VolumeInfo maybeInitializeVolume(StorageManager sm, Bundle bundle) {
final String volumeId = bundle.getString(VolumeInfo.EXTRA_VOLUME_ID,
VolumeInfo.ID_PRIVATE_INTERNAL);
VolumeInfo volume = sm.findVolumeById(volumeId);
return isVolumeValid(volume) ? volume : null;
}
private static boolean isVolumeValid(VolumeInfo volume) {
return (volume != null) && (volume.getType() == VolumeInfo.TYPE_PRIVATE)
&& volume.isMountedReadable();
}
} }

View File

@@ -25,11 +25,11 @@ import android.os.UserManager;
import android.os.storage.StorageManager; import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo; import android.os.storage.VolumeInfo;
import android.provider.SearchIndexableResource; import android.provider.SearchIndexableResource;
import android.support.annotation.VisibleForTesting;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.PackageManagerWrapperImpl; import com.android.settings.applications.PackageManagerWrapperImpl;
import com.android.settings.applications.UserManagerWrapper; import com.android.settings.applications.UserManagerWrapper;
import com.android.settings.applications.UserManagerWrapperImpl; import com.android.settings.applications.UserManagerWrapperImpl;
@@ -48,7 +48,6 @@ import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
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.Map;
public class StorageDashboardFragment extends DashboardFragment public class StorageDashboardFragment extends DashboardFragment
implements LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> { implements LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
@@ -61,11 +60,6 @@ public class StorageDashboardFragment extends DashboardFragment
private StorageItemPreferenceController mPreferenceController; private StorageItemPreferenceController mPreferenceController;
private List<PreferenceController> mSecondaryUsers; private List<PreferenceController> mSecondaryUsers;
private boolean isVolumeValid() {
return (mVolume != null) && (mVolume.getType() == VolumeInfo.TYPE_PRIVATE)
&& mVolume.isMountedReadable();
}
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@@ -101,7 +95,8 @@ public class StorageDashboardFragment extends DashboardFragment
// Initialize the storage sizes that we can quickly calc. // Initialize the storage sizes that we can quickly calc.
final Context context = getActivity(); final Context context = getActivity();
StorageManager sm = context.getSystemService(StorageManager.class); StorageManager sm = context.getSystemService(StorageManager.class);
if (!initializeVolume(sm, getArguments())) { mVolume = Utils.maybeInitializeVolume(sm, getArguments());
if (mVolume == null) {
getActivity().finish(); getActivity().finish();
return; return;
} }
@@ -156,17 +151,6 @@ public class StorageDashboardFragment extends DashboardFragment
return controllers; return controllers;
} }
/**
* Initializes the volume with a given bundle and returns if the volume is valid.
*/
@VisibleForTesting
boolean initializeVolume(StorageManager sm, Bundle bundle) {
String volumeId = bundle.getString(VolumeInfo.EXTRA_VOLUME_ID,
VolumeInfo.ID_PRIVATE_INTERNAL);
mVolume = sm.findVolumeById(volumeId);
return isVolumeValid();
}
/** /**
* Updates the secondary user controller sizes. * Updates the secondary user controller sizes.
*/ */
@@ -174,13 +158,10 @@ public class StorageDashboardFragment extends DashboardFragment
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++) {
PreferenceController controller = controllers.get(i); PreferenceController controller = controllers.get(i);
if (controller instanceof SecondaryUserController) { if (controller instanceof StorageAsyncLoader.ResultHandler) {
SecondaryUserController userController = (SecondaryUserController) controller; StorageAsyncLoader.ResultHandler userController =
int userId = userController.getUser().id; (StorageAsyncLoader.ResultHandler) controller;
StorageAsyncLoader.AppsStorageResult result = stats.get(userId); userController.handleResult(stats);
if (result != null) {
userController.setSize(result.externalStats.totalBytes);
}
} }
} }
} }

View File

@@ -0,0 +1,128 @@
/*
* 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.app.LoaderManager;
import android.content.Context;
import android.content.Loader;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.SparseArray;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.PackageManagerWrapperImpl;
import com.android.settings.applications.UserManagerWrapperImpl;
import com.android.settings.core.PreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader.AppsStorageResult;
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
import com.android.settingslib.applications.StorageStatsSource;
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
import java.util.ArrayList;
import java.util.List;
/**
* StorageProfileFragment is a fragment which shows the storage results for a profile of the
* primary user.
*/
public class StorageProfileFragment extends DashboardFragment
implements LoaderManager.LoaderCallbacks<SparseArray<AppsStorageResult>> {
private static final String TAG = "StorageProfileFragment";
public static final String USER_ID_EXTRA = "userId";
private static final int APPS_JOB_ID = 0;
private VolumeInfo mVolume;
private int mUserId;
private StorageItemPreferenceController mPreferenceController;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Bundle args = getArguments();
// Initialize the storage sizes that we can quickly calc.
final Context context = getActivity();
final StorageManager sm = context.getSystemService(StorageManager.class);
mVolume = Utils.maybeInitializeVolume(sm, args);
if (mVolume == null) {
getActivity().finish();
return;
}
mPreferenceController.setVolume(mVolume);
mUserId = args.getInt(USER_ID_EXTRA, UserHandle.myUserId());
mPreferenceController.setUserId(mUserId);
}
@Override
public void onResume() {
super.onResume();
getLoaderManager().initLoader(APPS_JOB_ID, Bundle.EMPTY, this);
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.SETTINGS_STORAGE_PROFILE;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.storage_profile_fragment;
}
@Override
protected List<PreferenceController> getPreferenceControllers(Context context) {
final List<PreferenceController> controllers = new ArrayList<>();
final StorageManager sm = context.getSystemService(StorageManager.class);
mPreferenceController = new StorageItemPreferenceController(context, this,
mVolume, new StorageManagerVolumeProvider(sm));
controllers.add(mPreferenceController);
return controllers;
}
@Override
public Loader<SparseArray<AppsStorageResult>> onCreateLoader(int id, Bundle args) {
Context context = getContext();
return new StorageAsyncLoader(context,
new UserManagerWrapperImpl(context.getSystemService(UserManager.class)),
mVolume.fsUuid,
new StorageStatsSource(context),
new PackageManagerWrapperImpl(context.getPackageManager()));
}
@Override
public void onLoadFinished(Loader<SparseArray<AppsStorageResult>> loader,
SparseArray<AppsStorageResult> result) {
mPreferenceController.onLoadFinished(result.get(mUserId));
}
@Override
public void onLoaderReset(Loader<SparseArray<AppsStorageResult>> loader) {
}
}

View File

@@ -23,6 +23,7 @@ import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.util.SparseArray;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.applications.UserManagerWrapper; import com.android.settings.applications.UserManagerWrapper;
@@ -35,10 +36,12 @@ import java.util.List;
* SecondaryUserController controls the preferences on the Storage screen which had to do with * SecondaryUserController controls the preferences on the Storage screen which had to do with
* secondary users. * secondary users.
*/ */
public class SecondaryUserController extends PreferenceController { public class SecondaryUserController extends PreferenceController implements
StorageAsyncLoader.ResultHandler {
// PreferenceGroupKey to try to add our preference onto. // PreferenceGroupKey to try to add our preference onto.
private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_secondary_users"; private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_secondary_users";
private static final String PREFERENCE_KEY_BASE = "pref_user_"; private static final String PREFERENCE_KEY_BASE = "pref_user_";
private static final int USER_PROFILE_INSERTION_LOCATION = 6;
private static final int SIZE_NOT_SET = -1; private static final int SIZE_NOT_SET = -1;
private @NonNull UserInfo mUser; private @NonNull UserInfo mUser;
@@ -59,7 +62,13 @@ public class SecondaryUserController extends PreferenceController {
List<UserInfo> infos = userManager.getUsers(); List<UserInfo> infos = userManager.getUsers();
for (int i = 0, size = infos.size(); i < size; i++) { for (int i = 0, size = infos.size(); i < size; i++) {
UserInfo info = infos.get(i); UserInfo info = infos.get(i);
if (info.equals(primaryUser)) {
continue;
}
if (info == null || Utils.isProfileOf(primaryUser, info)) { if (info == null || Utils.isProfileOf(primaryUser, info)) {
controllers.add(new UserProfileController(context, info,
USER_PROFILE_INSERTION_LOCATION));
continue; continue;
} }
@@ -131,6 +140,14 @@ public class SecondaryUserController extends PreferenceController {
} }
} }
public void handleResult(SparseArray<StorageAsyncLoader.AppsStorageResult> stats) {
int userId = getUser().id;
StorageAsyncLoader.AppsStorageResult result = stats.get(userId);
if (result != null) {
setSize(result.externalStats.totalBytes);
}
}
private static class NoSecondaryUserController extends PreferenceController { private static class NoSecondaryUserController extends PreferenceController {
public NoSecondaryUserController(Context context) { public NoSecondaryUserController(Context context) {
super(context); super(context);

View File

@@ -23,10 +23,9 @@ import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.os.UserHandle; import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.SparseArray;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import com.android.settings.applications.PackageManagerWrapper; import com.android.settings.applications.PackageManagerWrapper;
import com.android.settings.applications.UserManagerWrapper; import com.android.settings.applications.UserManagerWrapper;
@@ -34,7 +33,6 @@ import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.applications.StorageStatsSource; import com.android.settingslib.applications.StorageStatsSource;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* StorageAsyncLoader is a Loader which loads categorized app information and external stats for all * StorageAsyncLoader is a Loader which loads categorized app information and external stats for all
@@ -118,4 +116,12 @@ public class StorageAsyncLoader
public long otherAppsSize; public long otherAppsSize;
public StorageStatsSource.ExternalStorageStats externalStats; public StorageStatsSource.ExternalStorageStats externalStats;
} }
/**
* ResultHandler defines a destination of data which can handle a result from
* {@link StorageAsyncLoader}.
*/
public interface ResultHandler {
void handleResult(SparseArray<AppsStorageResult> result);
}
} }

View File

@@ -22,7 +22,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.VolumeInfo; import android.os.storage.VolumeInfo;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
@@ -36,12 +35,12 @@ import com.android.settings.Utils;
import com.android.settings.applications.ManageApplications; import com.android.settings.applications.ManageApplications;
import com.android.settings.core.PreferenceController; import com.android.settings.core.PreferenceController;
import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.deviceinfo.StorageMeasurement; import com.android.settingslib.deviceinfo.StorageMeasurement;
import com.android.settingslib.deviceinfo.StorageVolumeProvider; import com.android.settingslib.deviceinfo.StorageVolumeProvider;
import com.android.settingslib.applications.StorageStatsSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@@ -70,7 +69,7 @@ public class StorageItemPreferenceController extends PreferenceController {
private final MetricsFeatureProvider mMetricsFeatureProvider; private final MetricsFeatureProvider mMetricsFeatureProvider;
private final StorageVolumeProvider mSvp; private final StorageVolumeProvider mSvp;
private VolumeInfo mVolume; private VolumeInfo mVolume;
private final int mUserId; private int mUserId;
private long mSystemSize; private long mSystemSize;
private StorageItemPreferenceAlternate mPhotoPreference; private StorageItemPreferenceAlternate mPhotoPreference;
@@ -89,8 +88,7 @@ public class StorageItemPreferenceController extends PreferenceController {
mVolume = volume; mVolume = volume;
mSvp = svp; mSvp = svp;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
UserManager um = mContext.getSystemService(UserManager.class); mUserId = UserHandle.myUserId();
mUserId = um.getUserHandle();
} }
@Override @Override
@@ -157,6 +155,13 @@ public class StorageItemPreferenceController extends PreferenceController {
mVolume = volume; mVolume = volume;
} }
/**
* Sets the user id for which this preference controller is handling.
*/
public void setUserId(int userId) {
mUserId = userId;
}
@Override @Override
public void displayPreference(PreferenceScreen screen) { public void displayPreference(PreferenceScreen screen) {
mPhotoPreference = (StorageItemPreferenceAlternate) screen.findPreference(PHOTO_KEY); mPhotoPreference = (StorageItemPreferenceAlternate) screen.findPreference(PHOTO_KEY);
@@ -173,7 +178,9 @@ public class StorageItemPreferenceController extends PreferenceController {
mAudioPreference.setStorageSize(data.musicAppsSize + data.externalStats.audioBytes); mAudioPreference.setStorageSize(data.musicAppsSize + data.externalStats.audioBytes);
mGamePreference.setStorageSize(data.gamesSize); mGamePreference.setStorageSize(data.gamesSize);
mAppPreference.setStorageSize(data.otherAppsSize); mAppPreference.setStorageSize(data.otherAppsSize);
mSystemPreference.setStorageSize(mSystemSize); if (mSystemPreference != null) {
mSystemPreference.setStorageSize(mSystemSize);
}
long unattributedBytes = data.externalStats.totalBytes - data.externalStats.audioBytes long unattributedBytes = data.externalStats.totalBytes - data.externalStats.audioBytes
- data.externalStats.videoBytes - data.externalStats.imageBytes; - data.externalStats.videoBytes - data.externalStats.imageBytes;
@@ -188,6 +195,20 @@ public class StorageItemPreferenceController extends PreferenceController {
mSystemSize = systemSize; mSystemSize = systemSize;
} }
/**
* Returns a list of keys used by this preference controller.
*/
public static List<String> getUsedKeys() {
List<String> list = new ArrayList<>();
list.add(PHOTO_KEY);
list.add(AUDIO_KEY);
list.add(GAME_KEY);
list.add(OTHER_APPS_KEY);
list.add(SYSTEM_KEY);
list.add(FILES_KEY);
return list;
}
private Intent getPhotosIntent() { private Intent getPhotosIntent() {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW); intent.setAction(android.content.Intent.ACTION_VIEW);

View File

@@ -0,0 +1,106 @@
/*
* 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.storage;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.storage.VolumeInfo;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.util.SparseArray;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.Preconditions;
import com.android.settings.Utils;
import com.android.settings.core.PreferenceController;
import com.android.settings.deviceinfo.StorageProfileFragment;
import com.android.settingslib.drawer.SettingsDrawerActivity;
/**
* Defines a {@link PreferenceController} which handles a single profile of the primary user.
*/
public class UserProfileController extends PreferenceController implements
StorageAsyncLoader.ResultHandler {
private static final String PREFERENCE_KEY_BASE = "pref_profile_";
private StorageItemPreferenceAlternate mStoragePreference;
private UserInfo mUser;
private final int mPreferenceOrder;
public UserProfileController(Context context, UserInfo info, int preferenceOrder) {
super(context);
mUser = Preconditions.checkNotNull(info);
mPreferenceOrder = preferenceOrder;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public String getPreferenceKey() {
return PREFERENCE_KEY_BASE + mUser.id;
}
@Override
public void displayPreference(PreferenceScreen screen) {
mStoragePreference = new StorageItemPreferenceAlternate(mContext);
mStoragePreference.setOrder(mPreferenceOrder);
mStoragePreference.setKey(PREFERENCE_KEY_BASE + mUser.id);
mStoragePreference.setTitle(mUser.name);
screen.addPreference(mStoragePreference);
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (preference != null && mStoragePreference == preference) {
Bundle args = new Bundle(2);
args.putInt(StorageProfileFragment.USER_ID_EXTRA, mUser.id);
args.putString(VolumeInfo.EXTRA_VOLUME_ID, VolumeInfo.ID_PRIVATE_INTERNAL);
Intent intent = Utils.onBuildStartFragmentIntent(mContext,
StorageProfileFragment.class.getName(), args, null, 0,
mUser.name, false, MetricsProto.MetricsEvent.DEVICEINFO_STORAGE);
intent.putExtra(SettingsDrawerActivity.EXTRA_SHOW_MENU, true);
mContext.startActivity(intent);
return true;
}
return false;
}
@Override
public void handleResult(SparseArray<StorageAsyncLoader.AppsStorageResult> stats) {
Preconditions.checkNotNull(stats);
int userId = mUser.id;
StorageAsyncLoader.AppsStorageResult result = stats.get(userId);
if (result != null) {
setSize(result.externalStats.totalBytes);
}
}
/**
* Sets the size for the preference using a byte count.
*/
public void setSize(long size) {
if (mStoragePreference != null) {
mStoragePreference.setStorageSize(size);
}
}
}

View File

@@ -45,6 +45,7 @@ import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.datausage.DataUsageMeteredSettings; import com.android.settings.datausage.DataUsageMeteredSettings;
import com.android.settings.datausage.DataUsageSummary; import com.android.settings.datausage.DataUsageSummary;
import com.android.settings.deviceinfo.StorageDashboardFragment; import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.deviceinfo.StorageProfileFragment;
import com.android.settings.deviceinfo.StorageSettings; import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.display.ScreenZoomSettings; import com.android.settings.display.ScreenZoomSettings;
import com.android.settings.enterprise.EnterprisePrivacySettings; import com.android.settings.enterprise.EnterprisePrivacySettings;

View File

@@ -4,4 +4,5 @@ com.android.settings.notification.ZenModePrioritySettings
com.android.settings.inputmethod.InputAndGestureSettings com.android.settings.inputmethod.InputAndGestureSettings
com.android.settings.accounts.AccountDetailDashboardFragment com.android.settings.accounts.AccountDetailDashboardFragment
com.android.settings.gestures.GestureSettings com.android.settings.gestures.GestureSettings
com.android.settings.fuelgauge.PowerUsageDetail com.android.settings.fuelgauge.PowerUsageDetail
com.android.settings.deviceinfo.StorageProfileFragment

View File

@@ -1,13 +1,25 @@
package com.android.settings; package com.android.settings;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.LinkAddress; import android.net.LinkAddress;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.Network; import android.net.Network;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import java.net.InetAddress;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -16,9 +28,7 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat; import java.net.InetAddress;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -97,4 +107,13 @@ public class UtilsTest {
assertThat(Utils.formatElapsedTime(mContext, testMillis, false)).isEqualTo(expectedTime); assertThat(Utils.formatElapsedTime(mContext, testMillis, false)).isEqualTo(expectedTime);
} }
@Test
public void testInitializeVolumeDoesntBreakOnNullVolume() {
VolumeInfo info = new VolumeInfo("id", 0, new DiskInfo("id", 0), "");
StorageManager storageManager = mock(StorageManager.class, RETURNS_DEEP_STUBS);
when(storageManager.findVolumeById(anyString())).thenReturn(info);
Utils.maybeInitializeVolume(storageManager, new Bundle());
}
} }

View File

@@ -79,11 +79,4 @@ public class StorageDashboardFragmentTest {
assertThat(indexRes).isNotNull(); assertThat(indexRes).isNotNull();
assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId()); assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId());
} }
@Test
public void testInitializeVolumeDoesntBreakOnNullVolume() {
VolumeInfo info = new VolumeInfo("id", 0, new DiskInfo("id", 0), "");
when(mStorageManager.findVolumeById(anyString())).thenReturn(info);
mFragment.initializeVolume(mStorageManager, new Bundle());
}
} }

View File

@@ -28,11 +28,13 @@ import android.content.pm.UserInfo;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.util.SparseArray;
import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.applications.UserManagerWrapper; import com.android.settings.applications.UserManagerWrapper;
import com.android.settings.core.PreferenceController; import com.android.settings.core.PreferenceController;
import com.android.settingslib.applications.StorageStatsSource;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -130,7 +132,7 @@ public class SecondaryUserControllerTest {
} }
@Test @Test
public void profilesOfPrimaryUserAreIgnored() throws Exception { public void profilesOfPrimaryUserAreNotIgnored() throws Exception {
ArrayList<UserInfo> userInfos = new ArrayList<>(); ArrayList<UserInfo> userInfos = new ArrayList<>();
UserInfo secondaryUser = new UserInfo(); UserInfo secondaryUser = new UserInfo();
secondaryUser.id = mPrimaryUser.id; secondaryUser.id = mPrimaryUser.id;
@@ -142,7 +144,31 @@ public class SecondaryUserControllerTest {
List<PreferenceController> controllers = List<PreferenceController> controllers =
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager); SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager);
assertThat(controllers).hasSize(1); assertThat(controllers).hasSize(2);
assertThat(controllers.get(0) instanceof SecondaryUserController).isFalse(); assertThat(controllers.get(0) instanceof UserProfileController).isTrue();
assertThat(controllers.get(1) instanceof SecondaryUserController).isFalse();
}
@Test
public void controllerUpdatesPreferenceOnAcceptingResult() throws Exception {
mPrimaryUser.name = TEST_NAME;
mPrimaryUser.id = 10;
PreferenceScreen screen = mock(PreferenceScreen.class);
PreferenceGroup group = mock(PreferenceGroup.class);
when(screen.findPreference(anyString())).thenReturn(group);
when(group.getKey()).thenReturn(TARGET_PREFERENCE_GROUP_KEY);
mController.displayPreference(screen);
StorageAsyncLoader.AppsStorageResult userResult =
new StorageAsyncLoader.AppsStorageResult();
SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
userResult.externalStats = new StorageStatsSource.ExternalStorageStats(99, 33, 33, 33);
result.put(10, userResult);
mController.handleResult(result);
final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
verify(group).addPreference(argumentCaptor.capture());
Preference preference = argumentCaptor.getValue();
assertThat(preference.getSummary()).isEqualTo("99.00B");
} }
} }

View File

@@ -0,0 +1,123 @@
/*
* 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.storage;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.util.SparseArray;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.SubSettings;
import com.android.settings.TestConfig;
import com.android.settings.applications.UserManagerWrapper;
import com.android.settings.deviceinfo.StorageProfileFragment;
import com.android.settingslib.applications.StorageStatsSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class UserProfileControllerTest {
private static final String TEST_NAME = "Fred";
@Mock
private UserManagerWrapper mUserManager;
private Context mContext;
private UserProfileController mController;
private UserInfo mPrimaryProfile;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mPrimaryProfile = new UserInfo();
mController = new UserProfileController(mContext, mPrimaryProfile, 0);
}
@Test
public void controllerAddsPrimaryProfilePreference() throws Exception {
mPrimaryProfile.name = TEST_NAME;
mPrimaryProfile.id = 10;
PreferenceScreen screen = mock(PreferenceScreen.class);
mController.displayPreference(screen);
final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
verify(screen).addPreference(argumentCaptor.capture());
Preference preference = argumentCaptor.getValue();
assertThat(preference.getTitle()).isEqualTo(TEST_NAME);
assertThat(preference.getKey()).isEqualTo("pref_profile_10");
}
@Test
public void tappingProfilePreferenceSendsToStorageProfileFragment() throws Exception {
mPrimaryProfile.name = TEST_NAME;
mPrimaryProfile.id = 10;
PreferenceScreen screen = mock(PreferenceScreen.class);
mController.displayPreference(screen);
final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
verify(screen).addPreference(argumentCaptor.capture());
Preference preference = argumentCaptor.getValue();
assertThat(mController.handlePreferenceTreeClick(preference)).isTrue();
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivity(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
StorageProfileFragment.class.getName());
}
@Test
public void acceptingResultUpdatesPreferenceSize() throws Exception {
mPrimaryProfile.name = TEST_NAME;
mPrimaryProfile.id = 10;
PreferenceScreen screen = mock(PreferenceScreen.class);
mController.displayPreference(screen);
SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
StorageAsyncLoader.AppsStorageResult userResult =
new StorageAsyncLoader.AppsStorageResult();
userResult.externalStats = new StorageStatsSource.ExternalStorageStats(99, 33, 33, 33);
result.put(10, userResult);
mController.handleResult(result);
final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
verify(screen).addPreference(argumentCaptor.capture());
Preference preference = argumentCaptor.getValue();
assertThat(preference.getSummary()).isEqualTo("99.00B");
}
}