diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 6e36ee36733..e68d9f487e4 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -1364,6 +1364,16 @@ public final class Utils extends com.android.settingslib.Utils { } } + /** + * Returns true if the user should be hidden in Settings when it's in quiet mode. + */ + public static boolean shouldHideUser( + @NonNull UserHandle userHandle, @NonNull UserManager userManager) { + UserProperties userProperties = userManager.getUserProperties(userHandle); + return userProperties.getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN + && userManager.isQuietModeEnabled(userHandle); + } + private static FaceManager.RemovalCallback faceManagerRemovalCallback(int userId) { return new FaceManager.RemovalCallback() { @Override diff --git a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java index cd23103c5e4..7fbaf3c18f1 100644 --- a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java +++ b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java @@ -214,7 +214,7 @@ public class StylusDevicesController extends AbstractPreferenceController implem Intent intent = new Intent(Intent.ACTION_MANAGE_DEFAULT_APP).setPackage( packageName).putExtra(Intent.EXTRA_ROLE_NAME, RoleManager.ROLE_NOTES); - List users = getUserAndManagedProfiles(); + List users = getUserProfiles(); if (users.size() <= 1) { mContext.startActivity(intent); } else { @@ -311,22 +311,23 @@ public class StylusDevicesController extends AbstractPreferenceController implem return inputMethod != null && inputMethod.supportsStylusHandwriting(); } - private List getUserAndManagedProfiles() { + private List getUserProfiles() { UserManager um = mContext.getSystemService(UserManager.class); - final List userManagedProfiles = new ArrayList<>(); - // Add the current user, then add all the associated managed profiles. final UserHandle currentUser = Process.myUserHandle(); - userManagedProfiles.add(currentUser); + final List userProfiles = new ArrayList<>(); + userProfiles.add(currentUser); - final List userInfos = um.getUsers(); - for (UserInfo info : userInfos) { - int userId = info.id; - if (um.isManagedProfile(userId) - && um.getProfileParent(userId).id == currentUser.getIdentifier()) { - userManagedProfiles.add(UserHandle.of(userId)); + final List userInfos = um.getProfiles(currentUser.getIdentifier()); + for (UserInfo userInfo : userInfos) { + if (userInfo.isManagedProfile() + || (android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enablePrivateSpaceFeatures() + && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace() + && userInfo.isPrivateProfile())) { + userProfiles.add(userInfo.getUserHandle()); } } - return userManagedProfiles; + return userProfiles; } private UserHandle getDefaultNoteTaskProfile() { diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java index 4df1fdd3bb0..b276a39dffd 100644 --- a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java +++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java @@ -25,7 +25,6 @@ import android.content.DialogInterface.OnDismissListener; import android.content.DialogInterface.OnShowListener; import android.content.Intent; import android.content.pm.UserInfo; -import android.content.pm.UserProperties; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; @@ -42,6 +41,7 @@ import com.android.internal.widget.DialogTitle; import com.android.internal.widget.LinearLayoutManager; import com.android.internal.widget.RecyclerView; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.drawer.Tile; @@ -186,7 +186,7 @@ public class ProfileSelectDialog extends DialogFragment implements UserAdapter.O UserInfo userInfo = userManager.getUserInfo(userHandles.get(i).getIdentifier()); if (userInfo == null || userInfo.isCloneProfile() - || shouldHideUserInQuietMode(userHandles.get(i), userManager)) { + || Utils.shouldHideUser(userHandles.get(i), userManager)) { if (DEBUG) { Log.d(TAG, "Delete the user: " + userHandles.get(i).getIdentifier()); } @@ -219,7 +219,7 @@ public class ProfileSelectDialog extends DialogFragment implements UserAdapter.O UserInfo userInfo = userManager.getUserInfo(userHandle.getIdentifier()); if (userInfo == null || userInfo.isCloneProfile() - || shouldHideUserInQuietMode(userHandle, userManager)) { + || Utils.shouldHideUser(userHandle, userManager)) { if (DEBUG) { Log.d(TAG, "Delete the user: " + userHandle.getIdentifier()); } @@ -228,11 +228,4 @@ public class ProfileSelectDialog extends DialogFragment implements UserAdapter.O } } } - - private static boolean shouldHideUserInQuietMode( - UserHandle userHandle, UserManager userManager) { - UserProperties userProperties = userManager.getUserProperties(userHandle); - return userProperties.getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN - && userManager.isQuietModeEnabled(userHandle); - } } diff --git a/src/com/android/settings/dashboard/profileselector/UserAdapter.java b/src/com/android/settings/dashboard/profileselector/UserAdapter.java index 40d1a93e5d7..0fefa2f246e 100644 --- a/src/com/android/settings/dashboard/profileselector/UserAdapter.java +++ b/src/com/android/settings/dashboard/profileselector/UserAdapter.java @@ -64,7 +64,11 @@ public class UserAdapter extends BaseAdapter { UserInfo userInfo = um.getUserInfo(mUserHandle.getIdentifier()); int tintColor = Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.materialColorPrimary); - if (userInfo.isManagedProfile()) { + if (userInfo.isManagedProfile() + || (android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enablePrivateSpaceFeatures() + && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace() + && userInfo.isPrivateProfile())) { mIcon = context.getPackageManager().getUserBadgeForDensityNoBackground( userHandle, /* density= */ 0); mIcon.setTint(tintColor); @@ -88,6 +92,7 @@ public class UserAdapter extends BaseAdapter { () -> context.getString(com.android.settingslib.R.string.category_work)); } else if (android.os.Flags.allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceFeatures() + && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace() && mUserManager.getUserInfo(userId).isPrivateProfile()) { return resources.getString(PRIVATE_CATEGORY_HEADER, () -> context.getString(com.android.settingslib.R.string.category_private)); @@ -209,6 +214,7 @@ public class UserAdapter extends BaseAdapter { private static UserAdapter createUserAdapter( UserManager userManager, Context context, List userProfiles) { + updateUserHandlesIfNeeded(userManager, userProfiles); ArrayList userDetails = new ArrayList<>(userProfiles.size()); for (UserHandle userProfile : userProfiles) { userDetails.add(new UserDetails(userProfile, userManager, context)); @@ -216,6 +222,15 @@ public class UserAdapter extends BaseAdapter { return new UserAdapter(context, userDetails); } + private static void updateUserHandlesIfNeeded( + UserManager userManager, List userProfiles) { + for (int i = userProfiles.size() - 1; i >= 0; --i) { + if (com.android.settings.Utils.shouldHideUser(userProfiles.get(i), userManager)) { + userProfiles.remove(i); + } + } + } + static class ViewHolder extends RecyclerView.ViewHolder { private final ImageView mIconView; private final TextView mTitleView; diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java index 6c96f852ddc..c1cbf17f2c3 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java @@ -41,9 +41,12 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.content.pm.UserProperties; +import android.graphics.drawable.Drawable; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.Settings.Secure; import android.view.InputDevice; @@ -64,6 +67,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -77,8 +81,12 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) public class StylusDevicesControllerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final String NOTES_PACKAGE_NAME = "notes.package"; private static final CharSequence NOTES_APP_LABEL = "App Label"; + private static final int WORK_USER_ID = 1; + private static final int PRIVATE_USER_ID = 2; private Context mContext; private StylusDevicesController mController; @@ -95,6 +103,12 @@ public class StylusDevicesControllerTest { @Mock private UserManager mUserManager; @Mock + UserInfo mPersonalUserInfo; + @Mock + UserInfo mWorkUserInfo; + @Mock + UserInfo mPrivateUserInfo; + @Mock private RoleManager mRm; @Mock private Lifecycle mLifecycle; @@ -102,6 +116,8 @@ public class StylusDevicesControllerTest { private CachedBluetoothDevice mCachedBluetoothDevice; @Mock private BluetoothDevice mBluetoothDevice; + @Mock + private Drawable mIcon; @Before public void setUp() throws Exception { @@ -134,6 +150,7 @@ public class StylusDevicesControllerTest { when(mPm.getApplicationInfo(eq(NOTES_PACKAGE_NAME), any(PackageManager.ApplicationInfoFlags.class))).thenReturn(new ApplicationInfo()); when(mPm.getApplicationLabel(any(ApplicationInfo.class))).thenReturn(NOTES_APP_LABEL); + when(mPm.getUserBadgeForDensityNoBackground(any(), anyInt())).thenReturn(mIcon); when(mUserManager.getUsers()).thenReturn(Arrays.asList(new UserInfo(0, "default", 0))); when(mUserManager.isManagedProfile(anyInt())).thenReturn(false); @@ -371,14 +388,26 @@ public class StylusDevicesControllerTest { final String permissionPackageName = "permissions.package"; final UserHandle currentUser = Process.myUserHandle(); List userInfos = Arrays.asList( - new UserInfo(currentUser.getIdentifier(), "current", 0), - new UserInfo(1, "profile", UserInfo.FLAG_PROFILE) + mPersonalUserInfo, + mWorkUserInfo ); - when(mUserManager.getUsers()).thenReturn(userInfos); - when(mUserManager.isManagedProfile(1)).thenReturn(true); - when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(userInfos.get(0)); - when(mUserManager.getUserInfo(1)).thenReturn(userInfos.get(1)); - when(mUserManager.getProfileParent(1)).thenReturn(userInfos.get(0)); + UserProperties personalUserProperties = + new UserProperties.Builder() + .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_DEFAULT) + .build(); + UserProperties workUserProperties = + new UserProperties.Builder() + .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED) + .build(); + when(mWorkUserInfo.isManagedProfile()).thenReturn(true); + when(mWorkUserInfo.getUserHandle()).thenReturn(UserHandle.of(WORK_USER_ID)); + when(mUserManager.getProfiles(currentUser.getIdentifier())).thenReturn(userInfos); + when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(mPersonalUserInfo); + when(mUserManager.getUserInfo(WORK_USER_ID)).thenReturn(mWorkUserInfo); + when(mUserManager.getProfileParent(WORK_USER_ID)).thenReturn(mPersonalUserInfo); + when(mUserManager.getUserProperties(currentUser)).thenReturn(personalUserProperties); + when(mUserManager.getUserProperties(UserHandle.of(WORK_USER_ID))) + .thenReturn(workUserProperties); when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName); showScreen(mController); @@ -389,7 +418,55 @@ public class StylusDevicesControllerTest { } @Test - public void defaultNotesPreferenceClick_noManagedProfile_sendsManageDefaultRoleIntent() { + public void defaultNotesPreferenceClick_multiUsers_showsProfileSelectorDialog() { + mSetFlagsRule.enableFlags( + android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES, + android.multiuser.Flags.FLAG_HANDLE_INTERLEAVED_SETTINGS_FOR_PRIVATE_SPACE); + mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat); + final String permissionPackageName = "permissions.package"; + final UserHandle currentUser = Process.myUserHandle(); + List userInfos = Arrays.asList( + mPersonalUserInfo, + mPrivateUserInfo, + mWorkUserInfo + ); + UserProperties personalUserProperties = + new UserProperties.Builder() + .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_DEFAULT) + .build(); + UserProperties workUserProperties = + new UserProperties.Builder() + .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED) + .build(); + UserProperties privateUserProperties = + new UserProperties.Builder() + .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) + .build(); + when(mWorkUserInfo.isManagedProfile()).thenReturn(true); + when(mWorkUserInfo.getUserHandle()).thenReturn(UserHandle.of(WORK_USER_ID)); + when(mPrivateUserInfo.isPrivateProfile()).thenReturn(true); + when(mPrivateUserInfo.getUserHandle()).thenReturn(UserHandle.of(PRIVATE_USER_ID)); + when(mUserManager.getProfiles(currentUser.getIdentifier())).thenReturn(userInfos); + when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(mPersonalUserInfo); + when(mUserManager.getUserInfo(WORK_USER_ID)).thenReturn(mWorkUserInfo); + when(mUserManager.getUserInfo(PRIVATE_USER_ID)).thenReturn(mPrivateUserInfo); + when(mUserManager.getUserProperties(currentUser)).thenReturn(personalUserProperties); + when(mUserManager.getUserProperties(UserHandle.of(PRIVATE_USER_ID))) + .thenReturn(privateUserProperties); + when(mUserManager.getUserProperties(UserHandle.of(WORK_USER_ID))) + .thenReturn(workUserProperties); + when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName); + + showScreen(mController); + Preference defaultNotesPref = mPreferenceContainer.getPreference(0); + mController.onPreferenceClick(defaultNotesPref); + + assertTrue(mController.mDialog.isShowing()); + } + + @Test + public void defaultNotesPreferenceClick_noProfiles_sendsManageDefaultRoleIntent() { final ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat); final String permissionPackageName = "permissions.package"; @@ -398,7 +475,7 @@ public class StylusDevicesControllerTest { new UserInfo(currentUser.getIdentifier(), "current", 0), new UserInfo(1, "other", UserInfo.FLAG_FULL) ); - when(mUserManager.getUsers()).thenReturn(userInfos); + when(mUserManager.getProfiles(currentUser.getIdentifier())).thenReturn(userInfos); when(mUserManager.isManagedProfile(1)).thenReturn(false); when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(userInfos.get(0)); when(mUserManager.getUserInfo(1)).thenReturn(userInfos.get(1)); diff --git a/tests/robotests/src/com/android/settings/dashboard/profileselector/UserAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/profileselector/UserAdapterTest.java index 2fb5e03fa7c..b6ac410ece9 100644 --- a/tests/robotests/src/com/android/settings/dashboard/profileselector/UserAdapterTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/profileselector/UserAdapterTest.java @@ -24,8 +24,10 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.UserInfo; +import android.content.pm.UserProperties; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.flag.junit.SetFlagsRule; import android.widget.FrameLayout; import android.widget.TextView; @@ -52,9 +54,12 @@ import java.util.ArrayList; public class UserAdapterTest { @Rule public MockitoRule mRule = MockitoJUnit.rule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final int mPersonalUserId = UserHandle.myUserId(); private static final int WORK_USER_ID = 1; + private static final int PRIVATE_USER_ID = 2; @Mock private UserManager mUserManager; @@ -64,6 +69,8 @@ public class UserAdapterTest { @Mock private UserInfo mWorkUserInfo; + @Mock + private UserInfo mPrivateUserInfo; @Mock private UserAdapter.OnClickListener mOnClickListener; @@ -71,11 +78,31 @@ public class UserAdapterTest { @Spy private Context mContext = ApplicationProvider.getApplicationContext(); + private UserProperties mPersonalUserProperties = + new UserProperties.Builder() + .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_DEFAULT) + .build(); + private UserProperties mWorkUserProperties = + new UserProperties.Builder() + .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_PAUSED) + .build(); + private UserProperties mPrivateUserProperties = + new UserProperties.Builder() + .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) + .build(); + @Before public void setUp() { when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); when(mUserManager.getUserInfo(mPersonalUserId)).thenReturn(mPersonalUserInfo); when(mUserManager.getUserInfo(WORK_USER_ID)).thenReturn(mWorkUserInfo); + when(mUserManager.getUserInfo(PRIVATE_USER_ID)).thenReturn(mPrivateUserInfo); + when(mUserManager.getUserProperties(UserHandle.of(mPersonalUserId))) + .thenReturn(mPersonalUserProperties); + when(mUserManager.getUserProperties(UserHandle.of(WORK_USER_ID))) + .thenReturn(mWorkUserProperties); + when(mUserManager.getUserProperties(UserHandle.of(PRIVATE_USER_ID))) + .thenReturn(mPrivateUserProperties); } @Test @@ -102,6 +129,48 @@ public class UserAdapterTest { assertThat(userSpinnerAdapter.getUserHandle(1).getIdentifier()).isEqualTo(WORK_USER_ID); } + @Test + public void createUserSpinnerAdapter_withWorkAndPrivateProfiles_shouldSucceed() { + mSetFlagsRule.enableFlags( + android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES, + android.multiuser.Flags.FLAG_HANDLE_INTERLEAVED_SETTINGS_FOR_PRIVATE_SPACE); + when(mUserManager.getUserProfiles()).thenReturn( + Lists.newArrayList( + UserHandle.of(mPersonalUserId), + UserHandle.of(WORK_USER_ID), + UserHandle.of(PRIVATE_USER_ID))); + + UserAdapter userSpinnerAdapter = + UserAdapter.createUserSpinnerAdapter(mUserManager, mContext); + + assertThat(userSpinnerAdapter.getCount()).isEqualTo(3); + assertThat(userSpinnerAdapter.getUserHandle(0).getIdentifier()).isEqualTo(mPersonalUserId); + assertThat(userSpinnerAdapter.getUserHandle(1).getIdentifier()).isEqualTo(WORK_USER_ID); + assertThat(userSpinnerAdapter.getUserHandle(2).getIdentifier()).isEqualTo(PRIVATE_USER_ID); + } + + @Test + public void createUserSpinnerAdapter_withWorkAndQuietPrivateProfile_shouldShowTwoProfiles() { + mSetFlagsRule.enableFlags( + android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES, + android.multiuser.Flags.FLAG_HANDLE_INTERLEAVED_SETTINGS_FOR_PRIVATE_SPACE); + when(mUserManager.getUserProfiles()).thenReturn( + Lists.newArrayList( + UserHandle.of(mPersonalUserId), + UserHandle.of(WORK_USER_ID), + UserHandle.of(PRIVATE_USER_ID))); + when(mUserManager.isQuietModeEnabled(UserHandle.of(PRIVATE_USER_ID))).thenReturn(true); + + UserAdapter userSpinnerAdapter = + UserAdapter.createUserSpinnerAdapter(mUserManager, mContext); + + assertThat(userSpinnerAdapter.getCount()).isEqualTo(2); + assertThat(userSpinnerAdapter.getUserHandle(0).getIdentifier()).isEqualTo(mPersonalUserId); + assertThat(userSpinnerAdapter.getUserHandle(1).getIdentifier()).isEqualTo(WORK_USER_ID); + } + @Test public void createUserRecycleViewAdapter_canBindViewHolderCorrectly() { ArrayList userHandles =