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
diff --git a/res/xml/storage_dashboard_fragment.xml b/res/xml/storage_dashboard_fragment.xml
index 271d31b..fedc77f 100644
--- a/res/xml/storage_dashboard_fragment.xml
+++ b/res/xml/storage_dashboard_fragment.xml
@@ -16,40 +16,50 @@
<PreferenceScreen
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
- android:key="pref_summary" />
+ android:key="pref_summary"
+ android:order="0" />
<com.android.settings.deviceinfo.storage.StorageItemPreferenceAlternate
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
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
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
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
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
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>
<PreferenceCategory
android:key="pref_secondary_users"
- android:title="@string/storage_other_users" />
+ android:title="@string/storage_other_users"
+ android:order="200" />
<Preference
android:key="manage_storage"
android:title="@string/storage_menu_manage"
android:icon="@drawable/ic_settings_storage"
- android:fragment="com.android.settings.deletionhelper.AutomaticStorageManagerSettings">
+ android:fragment="com.android.settings.deletionhelper.AutomaticStorageManagerSettings"
+ android:order="300" >
</Preference>
</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/storage_profile_fragment.xml b/res/xml/storage_profile_fragment.xml
new file mode 100644
index 0000000..d0c5c3a
--- /dev/null
+++ b/res/xml/storage_profile_fragment.xml
@@ -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>
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 124c441..350ab9c 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -65,6 +65,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
import android.preference.PreferenceFrameLayout;
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Contacts;
@@ -1258,4 +1259,21 @@
(user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
&& 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();
+ }
}
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index d65eb75..72e1493 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -25,11 +25,11 @@
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.provider.SearchIndexableResource;
-import android.support.annotation.VisibleForTesting;
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.UserManagerWrapper;
import com.android.settings.applications.UserManagerWrapperImpl;
@@ -48,7 +48,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
public class StorageDashboardFragment extends DashboardFragment
implements LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
@@ -61,11 +60,6 @@
private StorageItemPreferenceController mPreferenceController;
private List<PreferenceController> mSecondaryUsers;
- private boolean isVolumeValid() {
- return (mVolume != null) && (mVolume.getType() == VolumeInfo.TYPE_PRIVATE)
- && mVolume.isMountedReadable();
- }
-
@Override
public void onResume() {
super.onResume();
@@ -101,7 +95,8 @@
// Initialize the storage sizes that we can quickly calc.
final Context context = getActivity();
StorageManager sm = context.getSystemService(StorageManager.class);
- if (!initializeVolume(sm, getArguments())) {
+ mVolume = Utils.maybeInitializeVolume(sm, getArguments());
+ if (mVolume == null) {
getActivity().finish();
return;
}
@@ -157,30 +152,16 @@
}
/**
- * 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.
*/
private void updateSecondaryUserControllers(List<PreferenceController> controllers,
SparseArray<StorageAsyncLoader.AppsStorageResult> stats) {
for (int i = 0, size = controllers.size(); i < size; i++) {
PreferenceController controller = controllers.get(i);
- if (controller instanceof SecondaryUserController) {
- SecondaryUserController userController = (SecondaryUserController) controller;
- int userId = userController.getUser().id;
- StorageAsyncLoader.AppsStorageResult result = stats.get(userId);
- if (result != null) {
- userController.setSize(result.externalStats.totalBytes);
- }
+ if (controller instanceof StorageAsyncLoader.ResultHandler) {
+ StorageAsyncLoader.ResultHandler userController =
+ (StorageAsyncLoader.ResultHandler) controller;
+ userController.handleResult(stats);
}
}
}
diff --git a/src/com/android/settings/deviceinfo/StorageProfileFragment.java b/src/com/android/settings/deviceinfo/StorageProfileFragment.java
new file mode 100644
index 0000000..6ae03da
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/StorageProfileFragment.java
@@ -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) {
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
index d45c6e3..a5e8373 100644
--- a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
+++ b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
@@ -23,6 +23,7 @@
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen;
+import android.util.SparseArray;
import com.android.settings.Utils;
import com.android.settings.applications.UserManagerWrapper;
@@ -35,10 +36,12 @@
* SecondaryUserController controls the preferences on the Storage screen which had to do with
* secondary users.
*/
-public class SecondaryUserController extends PreferenceController {
+public class SecondaryUserController extends PreferenceController implements
+ StorageAsyncLoader.ResultHandler {
// PreferenceGroupKey to try to add our preference onto.
private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_secondary_users";
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 @NonNull UserInfo mUser;
@@ -59,7 +62,13 @@
List<UserInfo> infos = userManager.getUsers();
for (int i = 0, size = infos.size(); i < size; i++) {
UserInfo info = infos.get(i);
+ if (info.equals(primaryUser)) {
+ continue;
+ }
+
if (info == null || Utils.isProfileOf(primaryUser, info)) {
+ controllers.add(new UserProfileController(context, info,
+ USER_PROFILE_INSERTION_LOCATION));
continue;
}
@@ -131,6 +140,14 @@
}
}
+ 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 {
public NoSecondaryUserController(Context context) {
super(context);
diff --git a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
index a60831b..d5d96a5 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
@@ -23,10 +23,9 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.UserInfo;
import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.SparseArray;
import android.util.Log;
+import android.util.SparseArray;
import com.android.settings.applications.PackageManagerWrapper;
import com.android.settings.applications.UserManagerWrapper;
@@ -34,7 +33,6 @@
import com.android.settingslib.applications.StorageStatsSource;
import java.util.List;
-import java.util.Map;
/**
* StorageAsyncLoader is a Loader which loads categorized app information and external stats for all
@@ -118,4 +116,12 @@
public long otherAppsSize;
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);
+ }
}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 88ba152..e31d968 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -22,7 +22,6 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
-import android.os.UserManager;
import android.os.storage.VolumeInfo;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
@@ -36,12 +35,12 @@
import com.android.settings.applications.ManageApplications;
import com.android.settings.core.PreferenceController;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
-
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.deviceinfo.StorageMeasurement;
import com.android.settingslib.deviceinfo.StorageVolumeProvider;
-import com.android.settingslib.applications.StorageStatsSource;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
/**
@@ -70,7 +69,7 @@
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final StorageVolumeProvider mSvp;
private VolumeInfo mVolume;
- private final int mUserId;
+ private int mUserId;
private long mSystemSize;
private StorageItemPreferenceAlternate mPhotoPreference;
@@ -89,8 +88,7 @@
mVolume = volume;
mSvp = svp;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
- UserManager um = mContext.getSystemService(UserManager.class);
- mUserId = um.getUserHandle();
+ mUserId = UserHandle.myUserId();
}
@Override
@@ -157,6 +155,13 @@
mVolume = volume;
}
+ /**
+ * Sets the user id for which this preference controller is handling.
+ */
+ public void setUserId(int userId) {
+ mUserId = userId;
+ }
+
@Override
public void displayPreference(PreferenceScreen screen) {
mPhotoPreference = (StorageItemPreferenceAlternate) screen.findPreference(PHOTO_KEY);
@@ -173,7 +178,9 @@
mAudioPreference.setStorageSize(data.musicAppsSize + data.externalStats.audioBytes);
mGamePreference.setStorageSize(data.gamesSize);
mAppPreference.setStorageSize(data.otherAppsSize);
- mSystemPreference.setStorageSize(mSystemSize);
+ if (mSystemPreference != null) {
+ mSystemPreference.setStorageSize(mSystemSize);
+ }
long unattributedBytes = data.externalStats.totalBytes - data.externalStats.audioBytes
- data.externalStats.videoBytes - data.externalStats.imageBytes;
@@ -188,6 +195,20 @@
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() {
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
diff --git a/src/com/android/settings/deviceinfo/storage/UserProfileController.java b/src/com/android/settings/deviceinfo/storage/UserProfileController.java
new file mode 100644
index 0000000..5da9fec
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/UserProfileController.java
@@ -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);
+ }
+ }
+}
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 07e1459..956ac0b 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -45,6 +45,7 @@
import com.android.settings.datausage.DataUsageMeteredSettings;
import com.android.settings.datausage.DataUsageSummary;
import com.android.settings.deviceinfo.StorageDashboardFragment;
+import com.android.settings.deviceinfo.StorageProfileFragment;
import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.display.ScreenZoomSettings;
import com.android.settings.enterprise.EnterprisePrivacySettings;
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index 5e134bf..76d1013 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -4,4 +4,5 @@
com.android.settings.inputmethod.InputAndGestureSettings
com.android.settings.accounts.AccountDetailDashboardFragment
com.android.settings.gestures.GestureSettings
-com.android.settings.fuelgauge.PowerUsageDetail
\ No newline at end of file
+com.android.settings.fuelgauge.PowerUsageDetail
+com.android.settings.deviceinfo.StorageProfileFragment
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index 90a6e05..324e751 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -1,13 +1,25 @@
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.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
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 java.net.InetAddress;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -16,9 +28,7 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
+import java.net.InetAddress;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -97,4 +107,13 @@
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());
+ }
}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java
index f16304e..e2a46de 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageDashboardFragmentTest.java
@@ -79,11 +79,4 @@
assertThat(indexRes).isNotNull();
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());
- }
}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
index b11132d..7222f53 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
@@ -28,11 +28,13 @@
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.support.v7.preference.PreferenceScreen;
+import android.util.SparseArray;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.applications.UserManagerWrapper;
import com.android.settings.core.PreferenceController;
+import com.android.settingslib.applications.StorageStatsSource;
import org.junit.Before;
import org.junit.Test;
@@ -130,7 +132,7 @@
}
@Test
- public void profilesOfPrimaryUserAreIgnored() throws Exception {
+ public void profilesOfPrimaryUserAreNotIgnored() throws Exception {
ArrayList<UserInfo> userInfos = new ArrayList<>();
UserInfo secondaryUser = new UserInfo();
secondaryUser.id = mPrimaryUser.id;
@@ -142,7 +144,31 @@
List<PreferenceController> controllers =
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager);
- assertThat(controllers).hasSize(1);
- assertThat(controllers.get(0) instanceof SecondaryUserController).isFalse();
+ assertThat(controllers).hasSize(2);
+ 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");
}
}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java
new file mode 100644
index 0000000..2cd4f76
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/UserProfileControllerTest.java
@@ -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");
+ }
+}
\ No newline at end of file