From dfed8a2acb62fce32eb6cf6e1665b98696cdd992 Mon Sep 17 00:00:00 2001 From: Daniel Nishi Date: Thu, 19 Jan 2017 16:32:41 -0800 Subject: [PATCH] Add support for visualizing secondary users. This functionality adds the secondary users to the screen, but currently does not populate the information for them. Once the external stats query works, I will add a loader which will populate this information. This also does not cover work profiles. Support for that is forthcoming. Bug: 34715777, 34225103 Test: Settings Robotest Change-Id: Ib9b692b214f5ce5d303dfd64516381443d4acebd --- res/xml/storage_dashboard_fragment.xml | 3 + src/com/android/settings/Utils.java | 12 ++ .../applications/UserManagerWrapper.java | 32 ++++ .../applications/UserManagerWrapperImpl.java | 40 +++++ .../deviceinfo/PrivateVolumeSettings.java | 10 +- .../deviceinfo/StorageDashboardFragment.java | 8 + .../storage/SecondaryUserController.java | 141 +++++++++++++++++ .../StorageItemPreferenceController.java | 3 + .../storage/SecondaryUserControllerTest.java | 149 ++++++++++++++++++ 9 files changed, 390 insertions(+), 8 deletions(-) create mode 100644 src/com/android/settings/applications/UserManagerWrapper.java create mode 100644 src/com/android/settings/applications/UserManagerWrapperImpl.java create mode 100644 src/com/android/settings/deviceinfo/storage/SecondaryUserController.java create mode 100644 tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java diff --git a/res/xml/storage_dashboard_fragment.xml b/res/xml/storage_dashboard_fragment.xml index e08ca961fd9..271d31bd859 100644 --- a/res/xml/storage_dashboard_fragment.xml +++ b/res/xml/storage_dashboard_fragment.xml @@ -43,6 +43,9 @@ android:key="pref_system" android:title="@string/storage_detail_system"> + getUsers(); +} diff --git a/src/com/android/settings/applications/UserManagerWrapperImpl.java b/src/com/android/settings/applications/UserManagerWrapperImpl.java new file mode 100644 index 00000000000..14ea64ae345 --- /dev/null +++ b/src/com/android/settings/applications/UserManagerWrapperImpl.java @@ -0,0 +1,40 @@ +/* + * 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.applications; + +import android.content.pm.UserInfo; +import android.os.UserManager; + +import java.util.List; + +public class UserManagerWrapperImpl implements UserManagerWrapper { + private UserManager mUserManager; + + public UserManagerWrapperImpl(UserManager userManager) { + mUserManager = userManager; + } + + @Override + public UserInfo getPrimaryUser() { + return mUserManager.getPrimaryUser(); + } + + @Override + public List getUsers() { + return mUserManager.getUsers(); + } +} diff --git a/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java b/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java index 6c3b9e62450..eb07a7fd4af 100644 --- a/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java +++ b/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java @@ -228,7 +228,7 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment { // Add current user and its profiles first for (int userIndex = 0; userIndex < userCount; ++userIndex) { final UserInfo userInfo = allUsers.get(userIndex); - if (isProfileOf(mCurrentUser, userInfo)) { + if (Utils.isProfileOf(mCurrentUser, userInfo)) { final PreferenceGroup details = showHeaders ? addCategory(screen, userInfo.name) : screen; addDetailItems(details, showShared, userInfo.id); @@ -242,7 +242,7 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment { getText(R.string.storage_other_users)); for (int userIndex = 0; userIndex < userCount; ++userIndex) { final UserInfo userInfo = allUsers.get(userIndex); - if (!isProfileOf(mCurrentUser, userInfo)) { + if (!Utils.isProfileOf(mCurrentUser, userInfo)) { addItem(otherUsers, /* titleRes */ 0, userInfo.name, userInfo.id); } } @@ -649,12 +649,6 @@ public class PrivateVolumeSettings extends SettingsPreferenceFragment { pref.setStorageSize(size, mTotalSize); } - private boolean isProfileOf(UserInfo user, UserInfo profile) { - return user.id == profile.id || - (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID - && user.profileGroupId == profile.profileGroupId); - } - private static long totalValues(MeasurementDetails details, int userId, String... keys) { long total = 0; HashMap map = details.mediaSize.get(userId); diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java index 914a8fe535a..0160534f013 100644 --- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java +++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java @@ -18,6 +18,7 @@ package com.android.settings.deviceinfo; import android.content.Context; import android.os.Bundle; +import android.os.UserManager; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.provider.SearchIndexableResource; @@ -25,8 +26,11 @@ import android.support.annotation.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +import com.android.settings.applications.UserManagerWrapper; +import com.android.settings.applications.UserManagerWrapperImpl; import com.android.settings.core.PreferenceController; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.deviceinfo.storage.SecondaryUserController; import com.android.settings.deviceinfo.storage.StorageItemPreferenceController; import com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController; import com.android.settings.overlay.FeatureFactory; @@ -110,6 +114,10 @@ public class StorageDashboardFragment extends DashboardFragment { mPreferenceController = new StorageItemPreferenceController(context, this, mVolume, new StorageManagerVolumeProvider(sm)); controllers.add(mPreferenceController); + + UserManagerWrapper userManager = + new UserManagerWrapperImpl(context.getSystemService(UserManager.class)); + SecondaryUserController.addAllSecondaryUserControllers(context, userManager, controllers); controllers.add(new ManageStoragePreferenceController(context)); return controllers; } diff --git a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java new file mode 100644 index 00000000000..b27dfa36058 --- /dev/null +++ b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java @@ -0,0 +1,141 @@ +/* + * 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.pm.UserInfo; +import android.os.UserManager; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.PreferenceGroup; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.Utils; +import com.android.settings.applications.UserManagerWrapper; +import com.android.settings.core.PreferenceController; + +import java.util.List; + +/** + * SecondaryUserController controls the preferences on the Storage screen which had to do with + * secondary users. + */ +public class SecondaryUserController extends PreferenceController { + // 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 UserInfo mUser; + private StorageItemPreferenceAlternate mPreference; + + /** + * Adds the appropriate controllers to a controller list for handling all secondary users on + * a device. + * @param context Context for initializing the preference controllers. + * @param controllers List of preference controllers for a Settings fragment. + */ + public static void addAllSecondaryUserControllers(Context context, + UserManagerWrapper userManager, List controllers) { + UserInfo primaryUser = userManager.getPrimaryUser(); + boolean addedUser = false; + List infos = userManager.getUsers(); + for (int i = 0, size = infos.size(); i < size; i++) { + UserInfo info = infos.get(i); + if (Utils.isProfileOf(primaryUser, info)) { + continue; + } + + controllers.add(new SecondaryUserController(context, info)); + addedUser = true; + } + + if (!addedUser) { + controllers.add(new NoSecondaryUserController(context)); + } + } + + /** + * Constructor for a given secondary user. + * @param context Context to initialize the underlying {@link PreferenceController}. + * @param info {@link UserInfo} for the secondary user which this controllers covers. + */ + @VisibleForTesting + SecondaryUserController(Context context, UserInfo info) { + super(context); + mUser = info; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + if (mPreference == null) { + mPreference = new StorageItemPreferenceAlternate(mContext); + + PreferenceGroup group = + (PreferenceGroup) screen.findPreference(TARGET_PREFERENCE_GROUP_KEY); + mPreference.setTitle(mUser.name); + mPreference.setKey(PREFERENCE_KEY_BASE + mUser.id); + group.setVisible(true); + group.addPreference(mPreference); + } + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return (mPreference != null) ? mPreference.getKey() : null; + } + + /** + * Sets the size for the preference. + * @param size Size in bytes. + */ + public void setSize(long size) { + if (mPreference != null) { + mPreference.setStorageSize(size); + } + } + + private static class NoSecondaryUserController extends PreferenceController { + public NoSecondaryUserController(Context context) { + super(context); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + PreferenceGroup group = + (PreferenceGroup) screen.findPreference(TARGET_PREFERENCE_GROUP_KEY); + if (group == null) { + return; + } + screen.removePreference(group); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return null; + } + + } +} diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java index 54a65dac7f2..7afd0618870 100644 --- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java +++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java @@ -110,6 +110,9 @@ public class StorageItemPreferenceController extends PreferenceController // TODO: Currently, this reflects the existing behavior for these toggles. // After the intermediate views are built, swap them in. Intent intent = null; + if (preference.getKey() == null) { + return false; + } switch (preference.getKey()) { case PHOTO_KEY: intent = getPhotosIntent(); diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java new file mode 100644 index 00000000000..43942a63d5e --- /dev/null +++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java @@ -0,0 +1,149 @@ +/* + * 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.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.UserInfo; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceGroup; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.applications.UserManagerWrapper; +import com.android.settings.core.PreferenceController; + +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; + +import java.util.ArrayList; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SecondaryUserControllerTest { + private static final String TEST_NAME = "Fred"; + private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_secondary_users"; + @Mock + private UserManagerWrapper mUserManager; + + private Context mContext; + private SecondaryUserController mController; + private UserInfo mPrimaryUser; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mPrimaryUser = new UserInfo(); + mController = new SecondaryUserController(mContext, mPrimaryUser); + } + + @Test + public void controllerAddsSecondaryUser() throws Exception { + mPrimaryUser.name = TEST_NAME; + 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); + + final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Preference.class); + verify(group).addPreference(argumentCaptor.capture()); + Preference preference = argumentCaptor.getValue(); + assertThat(preference.getTitle()).isEqualTo(TEST_NAME); + } + + @Test + public void controllerUpdatesSummaryOfNewPreference() throws Exception { + mPrimaryUser.name = TEST_NAME; + 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); + mController.setSize(10L); + final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Preference.class); + + verify(group).addPreference(argumentCaptor.capture()); + + Preference preference = argumentCaptor.getValue(); + assertThat(preference.getSummary()).isEqualTo("10.00B"); + } + + @Test + public void noSecondaryUserAddedIfNoneExist() throws Exception { + ArrayList userInfos = new ArrayList<>(); + userInfos.add(mPrimaryUser); + when(mUserManager.getPrimaryUser()).thenReturn(mPrimaryUser); + when(mUserManager.getUsers()).thenReturn(userInfos); + ArrayList controllers = new ArrayList<>(); + + SecondaryUserController.addAllSecondaryUserControllers(mContext, mUserManager, controllers); + + assertThat(controllers).hasSize(1); + // We should have the NoSecondaryUserController. + assertThat(controllers.get(0) instanceof SecondaryUserController).isFalse(); + } + + @Test + public void secondaryUserAddedIfHasDistinctId() throws Exception { + ArrayList userInfos = new ArrayList<>(); + UserInfo secondaryUser = new UserInfo(); + secondaryUser.id = 10; + secondaryUser.profileGroupId = 101010; // this just has to be something not 0 + userInfos.add(mPrimaryUser); + userInfos.add(secondaryUser); + when(mUserManager.getPrimaryUser()).thenReturn(mPrimaryUser); + when(mUserManager.getUsers()).thenReturn(userInfos); + ArrayList controllers = new ArrayList<>(); + + SecondaryUserController.addAllSecondaryUserControllers(mContext, mUserManager, controllers); + + assertThat(controllers).hasSize(1); + assertThat(controllers.get(0) instanceof SecondaryUserController).isTrue(); + } + + @Test + public void profilesOfPrimaryUserAreIgnored() throws Exception { + ArrayList userInfos = new ArrayList<>(); + UserInfo secondaryUser = new UserInfo(); + secondaryUser.id = mPrimaryUser.id; + userInfos.add(mPrimaryUser); + userInfos.add(secondaryUser); + when(mUserManager.getPrimaryUser()).thenReturn(mPrimaryUser); + when(mUserManager.getUsers()).thenReturn(userInfos); + ArrayList controllers = new ArrayList<>(); + + SecondaryUserController.addAllSecondaryUserControllers(mContext, mUserManager, controllers); + + assertThat(controllers).hasSize(1); + assertThat(controllers.get(0) instanceof SecondaryUserController).isFalse(); + } +}