diff --git a/packages/SystemUI/res/drawable/ic_account_circle.xml b/packages/SystemUI/res/drawable/ic_account_circle.xml new file mode 100644 index 0000000000000..5ca99f32771bd --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_account_circle.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_account_circle_filled.xml b/packages/SystemUI/res/drawable/ic_account_circle_filled.xml new file mode 100644 index 0000000000000..47c553b521238 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_account_circle_filled.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_add_supervised_user.xml b/packages/SystemUI/res/drawable/ic_add_supervised_user.xml new file mode 100644 index 0000000000000..627743ed16697 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_add_supervised_user.xml @@ -0,0 +1,19 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_manage_users.xml b/packages/SystemUI/res/drawable/ic_manage_users.xml new file mode 100644 index 0000000000000..3a0805de12306 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_manage_users.xml @@ -0,0 +1,23 @@ + + + + diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml index 7b95cf3cfa34b..1633e52e7a6c5 100644 --- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml +++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml @@ -21,6 +21,7 @@ android:id="@+id/user_switcher_root" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_marginBottom="64dp" android:layout_marginEnd="60dp" android:layout_marginStart="60dp"> diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen_popup_item.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen_popup_item.xml index 8d02429150f0f..401c4bdd55b69 100644 --- a/packages/SystemUI/res/layout/user_switcher_fullscreen_popup_item.xml +++ b/packages/SystemUI/res/layout/user_switcher_fullscreen_popup_item.xml @@ -29,17 +29,19 @@ diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 15147786e557d..faf518e73e6d7 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -88,6 +88,7 @@ #77000000 @android:color/system_neutral1_900 + @*android:color/text_color_primary_device_default_dark #E5FFFFFF diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index d39e295689864..6a34adadcffef 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2402,4 +2402,8 @@ Add + + @*android:string/supervised_user_creation_label + + Manage users diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 3ece240bc5766..7a7af4dcc9354 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -46,6 +46,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.telephony.TelephonyCallback; +import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -159,6 +160,7 @@ public class UserSwitcherController implements Dumpable { private final AtomicBoolean mGuestCreationScheduled; private FalsingManager mFalsingManager; private View mView; + private String mCreateSupervisedUserPackage; @Inject public UserSwitcherController(Context context, @@ -255,6 +257,9 @@ public class UserSwitcherController implements Dumpable { keyguardStateController.addCallback(mCallback); listenForCallState(); + mCreateSupervisedUserPackage = mContext.getString( + com.android.internal.R.string.config_supervisedUserCreationPackage); + dumpManager.registerDumpable(getClass().getSimpleName(), this); refreshUsers(UserHandle.USER_NULL); @@ -307,14 +312,10 @@ public class UserSwitcherController implements Dumpable { // User 0 boolean canSwitchUsers = mUserManager.getUserSwitchability( UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK; - UserInfo currentUserInfo = null; UserRecord guestRecord = null; for (UserInfo info : infos) { boolean isCurrent = currentId == info.id; - if (isCurrent) { - currentUserInfo = info; - } boolean switchToEnabled = canSwitchUsers || isCurrent; if (info.isEnabled()) { if (info.isGuest()) { @@ -322,7 +323,8 @@ public class UserSwitcherController implements Dumpable { // the icon shouldn't be enabled even if the user is current guestRecord = new UserRecord(info, null /* picture */, true /* isGuest */, isCurrent, false /* isAddUser */, - false /* isRestricted */, canSwitchUsers); + false /* isRestricted */, canSwitchUsers, + false /* isAddSupervisedUser */); } else if (info.supportsSwitchToByUser()) { Bitmap picture = bitmaps.get(info.id); if (picture == null) { @@ -337,7 +339,7 @@ public class UserSwitcherController implements Dumpable { } records.add(new UserRecord(info, picture, false /* isGuest */, isCurrent, false /* isAddUser */, false /* isRestricted */, - switchToEnabled)); + switchToEnabled, false /* isAddSupervisedUser */)); } } } @@ -345,19 +347,6 @@ public class UserSwitcherController implements Dumpable { Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true); } - boolean systemCanCreateUsers = !mUserManager.hasBaseUserRestriction( - UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM); - boolean currentUserCanCreateUsers = currentUserInfo != null - && (currentUserInfo.isAdmin() - || currentUserInfo.id == UserHandle.USER_SYSTEM) - && systemCanCreateUsers; - boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked; - boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers) - && guestRecord == null; - boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers) - && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY); - boolean createIsRestricted = !addUsersWhenLocked; - if (guestRecord == null) { if (mGuestUserAutoCreated) { // If mGuestIsResetting=true, the switch should be disabled since @@ -368,13 +357,14 @@ public class UserSwitcherController implements Dumpable { guestRecord = new UserRecord(null /* info */, null /* picture */, true /* isGuest */, false /* isCurrent */, false /* isAddUser */, false /* isRestricted */, - isSwitchToGuestEnabled); + isSwitchToGuestEnabled, false /* isAddSupervisedUser */); checkIfAddUserDisallowedByAdminOnly(guestRecord); records.add(guestRecord); - } else if (canCreateGuest) { + } else if (canCreateGuest(guestRecord != null)) { guestRecord = new UserRecord(null /* info */, null /* picture */, true /* isGuest */, false /* isCurrent */, - false /* isAddUser */, createIsRestricted, canSwitchUsers); + false /* isAddUser */, createIsRestricted(), canSwitchUsers, + false /* isAddSupervisedUser */); checkIfAddUserDisallowedByAdminOnly(guestRecord); records.add(guestRecord); } @@ -382,10 +372,19 @@ public class UserSwitcherController implements Dumpable { records.add(guestRecord); } - if (canCreateUser) { + if (canCreateUser()) { UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */, false /* isGuest */, false /* isCurrent */, true /* isAddUser */, - createIsRestricted, canSwitchUsers); + createIsRestricted(), canSwitchUsers, + false /* isAddSupervisedUser */); + checkIfAddUserDisallowedByAdminOnly(addUserRecord); + records.add(addUserRecord); + } + + if (canCreateSupervisedUser()) { + UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */, + false /* isGuest */, false /* isCurrent */, false /* isAddUser */, + createIsRestricted(), canSwitchUsers, true /* isAddSupervisedUser */); checkIfAddUserDisallowedByAdminOnly(addUserRecord); records.add(addUserRecord); } @@ -403,6 +402,40 @@ public class UserSwitcherController implements Dumpable { }.execute((SparseArray) bitmaps); } + boolean systemCanCreateUsers() { + return !mUserManager.hasBaseUserRestriction( + UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM); + } + + boolean currentUserCanCreateUsers() { + UserInfo currentUser = mUserTracker.getUserInfo(); + return currentUser != null + && (currentUser.isAdmin() || mUserTracker.getUserId() == UserHandle.USER_SYSTEM) + && systemCanCreateUsers(); + } + + boolean anyoneCanCreateUsers() { + return systemCanCreateUsers() && mAddUsersFromLockScreen; + } + + boolean canCreateGuest(boolean hasExistingGuest) { + return (currentUserCanCreateUsers() || anyoneCanCreateUsers()) + && !hasExistingGuest; + } + + boolean canCreateUser() { + return (currentUserCanCreateUsers() || anyoneCanCreateUsers()) + && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY); + } + + boolean createIsRestricted() { + return !mAddUsersFromLockScreen; + } + + boolean canCreateSupervisedUser() { + return !TextUtils.isEmpty(mCreateSupervisedUserPackage) && canCreateUser(); + } + private void pauseRefreshUsers() { if (!mPauseRefreshUsers) { mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS); @@ -485,6 +518,9 @@ public class UserSwitcherController implements Dumpable { } else if (record.isAddUser) { showAddUserDialog(dialogShower); return; + } else if (record.isAddSupervisedUser) { + startSupervisedUserActivity(); + return; } else { id = record.info.id; } @@ -561,6 +597,22 @@ public class UserSwitcherController implements Dumpable { } } + private void startSupervisedUserActivity() { + final Intent intent = new Intent() + .setAction(UserManager.ACTION_CREATE_SUPERVISED_USER) + .setPackage(mCreateSupervisedUserPackage) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // TODO(b/209659998): [to-be-removed] fallback activity for supervised user creation. + if (mContext.getPackageManager().resolveActivity(intent, 0) == null) { + intent.setPackage(null) + .setClassName("com.android.settings", + "com.android.settings.users.AddSupervisedUserActivity"); + } + + mContext.startActivity(intent); + } + private void listenForCallState() { mTelephonyListenerManager.addCallStateListener(mPhoneStateListener); } @@ -941,6 +993,8 @@ public class UserSwitcherController implements Dumpable { } } else if (item.isAddUser) { return context.getString(R.string.user_add_user); + } else if (item.isAddSupervisedUser) { + return context.getString(R.string.add_user_supervised); } else { return item.info.name; } @@ -955,9 +1009,11 @@ public class UserSwitcherController implements Dumpable { protected static Drawable getIconDrawable(Context context, UserRecord item) { int iconRes; if (item.isAddUser) { - iconRes = R.drawable.ic_add_circle; + iconRes = R.drawable.ic_account_circle; } else if (item.isGuest) { - iconRes = R.drawable.ic_avatar_guest_user; + iconRes = R.drawable.ic_account_circle_filled; + } else if (item.isAddSupervisedUser) { + iconRes = R.drawable.ic_add_supervised_user; } else { iconRes = R.drawable.ic_avatar_user; } @@ -1000,6 +1056,7 @@ public class UserSwitcherController implements Dumpable { public final boolean isGuest; public final boolean isCurrent; public final boolean isAddUser; + public final boolean isAddSupervisedUser; /** If true, the record is only visible to the owner and only when unlocked. */ public final boolean isRestricted; public boolean isDisabledByAdmin; @@ -1007,7 +1064,8 @@ public class UserSwitcherController implements Dumpable { public boolean isSwitchToEnabled; public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent, - boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled) { + boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled, + boolean isAddSupervisedUser) { this.info = info; this.picture = picture; this.isGuest = isGuest; @@ -1015,11 +1073,12 @@ public class UserSwitcherController implements Dumpable { this.isAddUser = isAddUser; this.isRestricted = isRestricted; this.isSwitchToEnabled = isSwitchToEnabled; + this.isAddSupervisedUser = isAddSupervisedUser; } public UserRecord copyWithIsCurrent(boolean _isCurrent) { return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted, - isSwitchToEnabled); + isSwitchToEnabled, isAddSupervisedUser); } public int resolveId() { @@ -1043,6 +1102,7 @@ public class UserSwitcherController implements Dumpable { } if (isGuest) sb.append(" "); if (isAddUser) sb.append(" "); + if (isAddSupervisedUser) sb.append(" "); if (isCurrent) sb.append(" "); if (picture != null) sb.append(" "); if (isRestricted) sb.append(" "); diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt index d6a8ab270b845..41da44acfbf0a 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt @@ -27,11 +27,10 @@ import android.graphics.drawable.InsetDrawable import android.graphics.drawable.LayerDrawable import android.os.Bundle import android.os.UserManager +import android.provider.Settings import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.WindowInsets -import android.view.WindowInsets.Type import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.ImageView @@ -71,8 +70,18 @@ class UserSwitcherActivity @Inject constructor( private lateinit var broadcastReceiver: BroadcastReceiver private var popupMenu: UserSwitcherPopupMenu? = null private lateinit var addButton: View - private var addUserItem: UserRecord? = null - private var addGuestItem: UserRecord? = null + private var addUserRecords = mutableListOf() + // When the add users options become available, insert another option to manage users + private val manageUserRecord = UserRecord( + null /* info */, + null /* picture */, + false /* isGuest */, + false /* isCurrent */, + false /* isAddUser */, + false /* isRestricted */, + false /* isSwitchToEnabled */, + false /* isAddSupervisedUser */ + ) private val adapter = object : BaseUserAdapter(userSwitcherController) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { @@ -104,7 +113,18 @@ class UserSwitcherActivity @Inject constructor( return view } + override fun getName(context: Context, item: UserRecord): String { + return if (item == manageUserRecord) { + getString(R.string.manage_users) + } else { + super.getName(context, item) + } + } + fun findUserIcon(item: UserRecord): Drawable { + if (item == manageUserRecord) { + return getDrawable(R.drawable.ic_manage_users) + } if (item.info == null) { return getIconDrawable(this@UserSwitcherActivity, item) } @@ -169,20 +189,11 @@ class UserSwitcherActivity @Inject constructor( super.onCreate(savedInstanceState) setContentView(R.layout.user_switcher_fullscreen) + window.decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) - parent = requireViewById(R.id.user_switcher_root).apply { - setOnApplyWindowInsetsListener { - v: View, insets: WindowInsets -> - v.apply { - val l = getPaddingLeft() - val t = getPaddingTop() - val r = getPaddingRight() - setPadding(l, t, r, insets.getInsets(Type.systemBars()).bottom) - } - - WindowInsets.CONSUMED - } - } + parent = requireViewById(R.id.user_switcher_root) requireViewById(R.id.cancel).apply { setOnClickListener { @@ -203,15 +214,19 @@ class UserSwitcherActivity @Inject constructor( private fun showPopupMenu() { val items = mutableListOf() - addUserItem?.let { items.add(it) } - addGuestItem?.let { items.add(it) } + addUserRecords.forEach { items.add(it) } var popupMenuAdapter = ItemAdapter( this, R.layout.user_switcher_fullscreen_popup_item, layoutInflater, { item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item) }, - { item: UserRecord -> adapter.findUserIcon(item) } + { item: UserRecord -> adapter.findUserIcon(item).mutate().apply { + setTint(resources.getColor( + R.color.user_switcher_fullscreen_popup_item_tint, + getTheme() + )) + } } ) popupMenuAdapter.addAll(items) @@ -225,10 +240,17 @@ class UserSwitcherActivity @Inject constructor( } // -1 for the header val item = popupMenuAdapter.getItem(pos - 1) - adapter.onUserListItemClicked(item) + if (item == manageUserRecord) { + val i = Intent().setAction(Settings.ACTION_USER_SETTINGS) + this@UserSwitcherActivity.startActivity(i) + } else { + adapter.onUserListItemClicked(item) + } dismiss() popupMenu = null + + this@UserSwitcherActivity.finish() } show() @@ -245,14 +267,15 @@ class UserSwitcherActivity @Inject constructor( } } parent.removeViews(start, count) + addUserRecords.clear() val flow = requireViewById(R.id.flow) for (i in 0 until adapter.getCount()) { val item = adapter.getItem(i) - if (item.isAddUser) { - addUserItem = item - } else if (item.isGuest && item.info == null) { - addGuestItem = item + if (item.isAddUser || + item.isAddSupervisedUser || + item.isGuest && item.info == null) { + addUserRecords.add(item) } else { val userView = adapter.getView(i, null, parent) userView.setId(View.generateViewId()) @@ -273,7 +296,8 @@ class UserSwitcherActivity @Inject constructor( } } - if (addUserItem != null || addGuestItem != null) { + if (!addUserRecords.isEmpty()) { + addUserRecords.add(manageUserRecord) addButton.visibility = View.VISIBLE } else { addButton.visibility = View.GONE diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt index 896354737e463..754a9342bfb0e 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt @@ -42,7 +42,7 @@ class UserSwitcherPopupMenu( setBackgroundDrawable( res.getDrawable(R.drawable.bouncer_user_switcher_popup_bg, context.getTheme()) ) - setModal(true) + setModal(false) setOverlapAnchor(true) } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index 24b01e079b428..6736bfd217404 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -307,7 +307,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { UserInfo info = new UserInfo(i /* id */, "Name: " + i, null /* iconPath */, 0 /* flags */); users.add(new UserRecord(info, null, false /* isGuest */, false /* isCurrent */, - false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */)); + false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */)); } return users; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt index 3a3d1546984d1..9b0142d6a8fe4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt @@ -147,5 +147,6 @@ class UserDetailViewAdapterTest : SysuiTestCase() { current, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt index e479882ac50a8..0dd6cbb7995ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt @@ -193,5 +193,6 @@ class KeyguardUserSwitcherAdapterTest : SysuiTestCase() { isCurrentUser, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt index 9a7e702152b49..9dabf6907f7cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt @@ -56,6 +56,7 @@ import com.android.systemui.util.time.FakeSystemClock import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -130,6 +131,21 @@ class UserSwitcherControllerTest : SysuiTestCase() { .thenReturn(true) `when`(notificationShadeWindowView.context).thenReturn(context) + // Since userSwitcherController involves InteractionJankMonitor. + // Let's fulfill the dependencies. + val mockedContext = mock(Context::class.java) + doReturn(mockedContext).`when`(notificationShadeWindowView).context + doReturn(true).`when`(notificationShadeWindowView).isAttachedToWindow + doNothing().`when`(threadedRenderer).addObserver(any()) + doNothing().`when`(threadedRenderer).removeObserver(any()) + doReturn(threadedRenderer).`when`(notificationShadeWindowView).threadedRenderer + + picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user)) + + setupController() + } + + private fun setupController() { userSwitcherController = UserSwitcherController( context, activityManager, @@ -153,18 +169,6 @@ class UserSwitcherControllerTest : SysuiTestCase() { dumpManager, dialogLaunchAnimator) userSwitcherController.mPauseRefreshUsers = true - - // Since userSwitcherController involves InteractionJankMonitor. - // Let's fulfill the dependencies. - val mockedContext = mock(Context::class.java) - doReturn(mockedContext).`when`(notificationShadeWindowView).context - doReturn(true).`when`(notificationShadeWindowView).isAttachedToWindow - doNothing().`when`(threadedRenderer).addObserver(any()) - doNothing().`when`(threadedRenderer).removeObserver(any()) - doReturn(threadedRenderer).`when`(notificationShadeWindowView).threadedRenderer - userSwitcherController.init(notificationShadeWindowView) - - picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user)) userSwitcherController.init(notificationShadeWindowView) } @@ -177,7 +181,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { false /* current */, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) `when`(userTracker.userId).thenReturn(ownerId) `when`(userTracker.userInfo).thenReturn(ownerInfo) @@ -196,7 +201,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { false /* current */, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) `when`(userTracker.userId).thenReturn(ownerId) `when`(userTracker.userInfo).thenReturn(ownerInfo) @@ -220,7 +226,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { false /* current */, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) `when`(userTracker.userId).thenReturn(ownerId) `when`(userTracker.userInfo).thenReturn(ownerInfo) @@ -240,7 +247,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { true /* current */, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) `when`(userTracker.userId).thenReturn(guestInfo.id) `when`(userTracker.userInfo).thenReturn(guestInfo) @@ -262,7 +270,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { true /* current */, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) `when`(userTracker.userId).thenReturn(guestInfo.id) `when`(userTracker.userInfo).thenReturn(guestInfo) @@ -283,7 +292,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { true /* current */, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) `when`(userTracker.userId).thenReturn(guestInfo.id) `when`(userTracker.userInfo).thenReturn(guestInfo) @@ -302,7 +312,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { true /* current */, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) `when`(userTracker.userId).thenReturn(guestId) `when`(userTracker.userInfo).thenReturn(guestInfo) @@ -323,7 +334,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { false /* current */, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) `when`(userTracker.userId).thenReturn(guestId) `when`(userTracker.userInfo).thenReturn(guestInfo) @@ -357,7 +369,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { false /* current */, false /* isAddUser */, false /* isRestricted */, - true /* isSwitchToEnabled */) + true /* isSwitchToEnabled */, + false /* isAddSupervisedUser */) `when`(userTracker.userId).thenReturn(guestId) `when`(userTracker.userInfo).thenReturn(guestInfo) @@ -389,7 +402,7 @@ class UserSwitcherControllerTest : SysuiTestCase() { userSwitcherController.users.add(UserSwitcherController.UserRecord( UserInfo(id, name, 0), null, false, isCurrent, false, - false, false + false, false, false )) } val bgUserName = "background_user" @@ -412,4 +425,42 @@ class UserSwitcherControllerTest : SysuiTestCase() { `when`(userTracker.userId).thenReturn(1) assertEquals(false, userSwitcherController.isSystemUser) } + + @Test + fun testCanCreateSupervisedUserWithConfiguredPackage() { + // GIVEN the supervised user creation package is configured + `when`(context.getString( + com.android.internal.R.string.config_supervisedUserCreationPackage)) + .thenReturn("some_pkg") + + // AND the current user is allowed to create new users + `when`(userTracker.userId).thenReturn(ownerId) + `when`(userTracker.userInfo).thenReturn(ownerInfo) + + // WHEN the controller is started with the above config + setupController() + testableLooper.processAllMessages() + + // THEN a supervised user can be constructed + assertTrue(userSwitcherController.canCreateSupervisedUser()) + } + + @Test + fun testCannotCreateSupervisedUserWithConfiguredPackage() { + // GIVEN the supervised user creation package is NOT configured + `when`(context.getString( + com.android.internal.R.string.config_supervisedUserCreationPackage)) + .thenReturn(null) + + // AND the current user is allowed to create new users + `when`(userTracker.userId).thenReturn(ownerId) + `when`(userTracker.userInfo).thenReturn(ownerInfo) + + // WHEN the controller is started with the above config + setupController() + testableLooper.processAllMessages() + + // THEN a supervised user can NOT be constructed + assertFalse(userSwitcherController.canCreateSupervisedUser()) + } }