Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt +82 −31 Original line number Diff line number Diff line Loading @@ -17,12 +17,21 @@ package com.android.systemui.statusbar.policy import android.content.Context import android.content.Intent import android.view.View import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.user.UserSwitchDialogController import com.android.systemui.user.data.source.UserRecord import com.android.systemui.user.domain.interactor.GuestUserInteractor import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.user.legacyhelper.data.LegacyUserDataHelper import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper import dagger.Lazy import java.io.PrintWriter import java.lang.ref.WeakReference Loading @@ -30,11 +39,17 @@ import javax.inject.Inject import kotlinx.coroutines.flow.Flow /** Implementation of [UserSwitcherController]. */ @SysUISingleton class UserSwitcherControllerImpl @Inject constructor( private val flags: FeatureFlags, @Application private val applicationContext: Context, flags: FeatureFlags, @Suppress("DEPRECATION") private val oldImpl: Lazy<UserSwitcherControllerOldImpl>, private val userInteractorLazy: Lazy<UserInteractor>, private val guestUserInteractorLazy: Lazy<GuestUserInteractor>, private val keyguardInteractorLazy: Lazy<KeyguardInteractor>, private val activityStarter: ActivityStarter, ) : UserSwitcherController { private val useInteractor: Boolean = Loading @@ -42,15 +57,21 @@ constructor( !flags.isEnabled(Flags.USER_INTERACTOR_AND_REPO_USE_CONTROLLER) private val _oldImpl: UserSwitcherControllerOldImpl get() = oldImpl.get() private val userInteractor: UserInteractor by lazy { userInteractorLazy.get() } private val guestUserInteractor: GuestUserInteractor by lazy { guestUserInteractorLazy.get() } private val keyguardInteractor: KeyguardInteractor by lazy { keyguardInteractorLazy.get() } private fun notYetImplemented(): Nothing { error("Not yet implemented!") private val callbackCompatMap = mutableMapOf<UserSwitcherController.UserSwitchCallback, UserInteractor.UserCallback>() private fun notSupported(): Nothing { error("Not supported in the new implementation!") } override val users: ArrayList<UserRecord> get() = if (useInteractor) { notYetImplemented() userInteractor.userRecords.value } else { _oldImpl.users } Loading @@ -58,15 +79,13 @@ constructor( override val isSimpleUserSwitcher: Boolean get() = if (useInteractor) { notYetImplemented() userInteractor.isSimpleUserSwitcher } else { _oldImpl.isSimpleUserSwitcher } override fun init(view: View) { if (useInteractor) { notYetImplemented() } else { if (!useInteractor) { _oldImpl.init(view) } } Loading @@ -74,7 +93,7 @@ constructor( override val currentUserRecord: UserRecord? get() = if (useInteractor) { notYetImplemented() userInteractor.selectedUserRecord.value } else { _oldImpl.currentUserRecord } Loading @@ -82,7 +101,14 @@ constructor( override val currentUserName: String? get() = if (useInteractor) { notYetImplemented() currentUserRecord?.let { LegacyUserUiHelper.getUserRecordName( context = applicationContext, record = it, isGuestUserAutoCreated = userInteractor.isGuestUserAutoCreated, isGuestUserResetting = userInteractor.isGuestUserResetting, ) } } else { _oldImpl.currentUserName } Loading @@ -92,7 +118,7 @@ constructor( dialogShower: UserSwitchDialogController.DialogShower? ) { if (useInteractor) { notYetImplemented() userInteractor.selectUser(userId) } else { _oldImpl.onUserSelected(userId, dialogShower) } Loading @@ -101,7 +127,7 @@ constructor( override val isAddUsersFromLockScreenEnabled: Flow<Boolean> get() = if (useInteractor) { notYetImplemented() notSupported() } else { _oldImpl.isAddUsersFromLockScreenEnabled } Loading @@ -109,7 +135,7 @@ constructor( override val isGuestUserAutoCreated: Boolean get() = if (useInteractor) { notYetImplemented() userInteractor.isGuestUserAutoCreated } else { _oldImpl.isGuestUserAutoCreated } Loading @@ -117,7 +143,7 @@ constructor( override val isGuestUserResetting: Boolean get() = if (useInteractor) { notYetImplemented() userInteractor.isGuestUserResetting } else { _oldImpl.isGuestUserResetting } Loading @@ -126,7 +152,7 @@ constructor( dialogShower: UserSwitchDialogController.DialogShower?, ) { if (useInteractor) { notYetImplemented() notSupported() } else { _oldImpl.createAndSwitchToGuestUser(dialogShower) } Loading @@ -134,7 +160,7 @@ constructor( override fun showAddUserDialog(dialogShower: UserSwitchDialogController.DialogShower?) { if (useInteractor) { notYetImplemented() notSupported() } else { _oldImpl.showAddUserDialog(dialogShower) } Loading @@ -142,23 +168,31 @@ constructor( override fun startSupervisedUserActivity() { if (useInteractor) { notYetImplemented() notSupported() } else { _oldImpl.startSupervisedUserActivity() } } override fun onDensityOrFontScaleChanged() { if (useInteractor) { notYetImplemented() } else { if (!useInteractor) { _oldImpl.onDensityOrFontScaleChanged() } } override fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) { if (useInteractor) { notYetImplemented() userInteractor.addCallback( object : UserInteractor.UserCallback { override fun isEvictable(): Boolean { return adapter.get() == null } override fun onUserStateChanged() { adapter.get()?.notifyDataSetChanged() } } ) } else { _oldImpl.addAdapter(adapter) } Loading @@ -169,7 +203,11 @@ constructor( dialogShower: UserSwitchDialogController.DialogShower?, ) { if (useInteractor) { notYetImplemented() if (LegacyUserDataHelper.isUser(record)) { userInteractor.selectUser(record.resolveId()) } else { userInteractor.executeAction(LegacyUserDataHelper.toUserActionModel(record)) } } else { _oldImpl.onUserListItemClicked(record, dialogShower) } Loading @@ -177,7 +215,10 @@ constructor( override fun removeGuestUser(guestUserId: Int, targetUserId: Int) { if (useInteractor) { notYetImplemented() userInteractor.removeGuestUser( guestUserId = guestUserId, targetUserId = targetUserId, ) } else { _oldImpl.removeGuestUser(guestUserId, targetUserId) } Loading @@ -189,7 +230,7 @@ constructor( forceRemoveGuestOnExit: Boolean ) { if (useInteractor) { notYetImplemented() userInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit) } else { _oldImpl.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit) } Loading @@ -197,7 +238,7 @@ constructor( override fun schedulePostBootGuestCreation() { if (useInteractor) { notYetImplemented() guestUserInteractor.onDeviceBootCompleted() } else { _oldImpl.schedulePostBootGuestCreation() } Loading @@ -206,14 +247,14 @@ constructor( override val isKeyguardShowing: Boolean get() = if (useInteractor) { notYetImplemented() keyguardInteractor.isKeyguardShowing() } else { _oldImpl.isKeyguardShowing } override fun startActivity(intent: Intent) { if (useInteractor) { notYetImplemented() activityStarter.startActivity(intent, /* dismissShade= */ false) } else { _oldImpl.startActivity(intent) } Loading @@ -221,7 +262,7 @@ constructor( override fun refreshUsers(forcePictureLoadForId: Int) { if (useInteractor) { notYetImplemented() userInteractor.refreshUsers() } else { _oldImpl.refreshUsers(forcePictureLoadForId) } Loading @@ -229,7 +270,14 @@ constructor( override fun addUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) { if (useInteractor) { notYetImplemented() val interactorCallback = object : UserInteractor.UserCallback { override fun onUserStateChanged() { callback.onUserSwitched() } } callbackCompatMap[callback] = interactorCallback userInteractor.addCallback(interactorCallback) } else { _oldImpl.addUserSwitchCallback(callback) } Loading @@ -237,7 +285,10 @@ constructor( override fun removeUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) { if (useInteractor) { notYetImplemented() val interactorCallback = callbackCompatMap.remove(callback) if (interactorCallback != null) { userInteractor.removeCallback(interactorCallback) } } else { _oldImpl.removeUserSwitchCallback(callback) } Loading @@ -245,7 +296,7 @@ constructor( override fun dump(pw: PrintWriter, args: Array<out String>) { if (useInteractor) { notYetImplemented() userInteractor.dump(pw) } else { _oldImpl.dump(pw, args) } Loading packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt +1 −1 Original line number Diff line number Diff line Loading @@ -173,7 +173,7 @@ constructor( } /** Removes the guest user from the device. */ private suspend fun remove( suspend fun remove( @UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int, showDialog: (ShowDialogRequestModel) -> Unit, Loading packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt +105 −44 Original line number Diff line number Diff line Loading @@ -52,8 +52,7 @@ import com.android.systemui.user.legacyhelper.data.LegacyUserDataHelper import com.android.systemui.user.shared.model.UserActionModel import com.android.systemui.user.shared.model.UserModel import com.android.systemui.util.kotlin.pairwise import java.util.Collections import java.util.WeakHashMap import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope Loading @@ -70,6 +69,9 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext /** Encapsulates business logic to interact with user data and systems. */ Loading @@ -96,7 +98,9 @@ constructor( * Defines interface for classes that can be notified when the state of users on the device is * changed. */ fun interface UserCallback { interface UserCallback { /** Returns `true` if this callback can be cleaned-up. */ fun isEvictable(): Boolean = false /** Notifies that the state of users on the device has changed. */ fun onUserStateChanged() } Loading @@ -110,7 +114,8 @@ constructor( com.android.internal.R.string.config_supervisedUserCreationPackage ) private val callbacks = Collections.newSetFromMap(WeakHashMap<UserCallback, Boolean>()) private val callbackMutex = Mutex() private val callbacks = mutableSetOf<UserCallback>() /** List of current on-device users to select from. */ val users: Flow<List<UserModel>> Loading Loading @@ -263,6 +268,7 @@ constructor( } ) } .onEach { notifyCallbacks() } .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, Loading Loading @@ -307,30 +313,6 @@ constructor( error("Not supported in the old implementation!") } fun addCallback(callback: UserCallback) { callbacks.add(callback) } fun removeCallback(callback: UserCallback) { callbacks.remove(callback) } fun onDialogShown() { _dialogShowRequests.value = null } fun onDialogDismissed() { _dialogDismissRequests.value = null } private fun showDialog(request: ShowDialogRequestModel) { _dialogShowRequests.value = request } private fun dismissDialog() { _dialogDismissRequests.value = Unit } init { if (isNewImpl) { refreshUsersScheduler.refreshIfNotPaused() Loading Loading @@ -364,6 +346,46 @@ constructor( } } fun addCallback(callback: UserCallback) { applicationScope.launch { callbackMutex.withLock { callbacks.add(callback) } } } fun removeCallback(callback: UserCallback) { applicationScope.launch { callbackMutex.withLock { callbacks.remove(callback) } } } fun refreshUsers() { refreshUsersScheduler.refreshIfNotPaused() } fun onDialogShown() { _dialogShowRequests.value = null } fun onDialogDismissed() { _dialogDismissRequests.value = null } fun dump(pw: PrintWriter) { pw.println("UserInteractor state:") pw.println(" lastSelectedNonGuestUserId=${repository.lastSelectedNonGuestUserId}") val users = userRecords.value.filter { it.info != null } pw.println(" userCount=${userRecords.value.count { LegacyUserDataHelper.isUser(it) }}") for (i in users.indices) { pw.println(" ${users[i]}") } val actions = userRecords.value.filter { it.info == null } pw.println(" actionCount=${userRecords.value.count { !LegacyUserDataHelper.isUser(it) }}") for (i in actions.indices) { pw.println(" ${actions[i]}") } pw.println("isSimpleUserSwitcher=$isSimpleUserSwitcher") pw.println("isGuestUserAutoCreated=$isGuestUserAutoCreated") } fun onDeviceBootCompleted() { guestUserInteractor.onDeviceBootCompleted() } Loading Loading @@ -460,6 +482,60 @@ constructor( } } fun exitGuestUser( @UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int, forceRemoveGuestOnExit: Boolean, ) { guestUserInteractor.exit( guestUserId = guestUserId, targetUserId = targetUserId, forceRemoveGuestOnExit = forceRemoveGuestOnExit, showDialog = this::showDialog, dismissDialog = this::dismissDialog, switchUser = this::switchUser, ) } fun removeGuestUser( @UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int, ) { applicationScope.launch { guestUserInteractor.remove( guestUserId = guestUserId, targetUserId = targetUserId, ::showDialog, ::dismissDialog, ::selectUser, ) } } private fun showDialog(request: ShowDialogRequestModel) { _dialogShowRequests.value = request } private fun dismissDialog() { _dialogDismissRequests.value = Unit } private fun notifyCallbacks() { applicationScope.launch { callbackMutex.withLock { val iterator = callbacks.iterator() while (iterator.hasNext()) { val callback = iterator.next() if (!callback.isEvictable()) { callback.onUserStateChanged() } else { iterator.remove() } } } } } private suspend fun toRecord( userInfo: UserInfo, selectedUserId: Int, Loading Loading @@ -498,21 +574,6 @@ constructor( ) } private fun exitGuestUser( @UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int, forceRemoveGuestOnExit: Boolean, ) { guestUserInteractor.exit( guestUserId = guestUserId, targetUserId = targetUserId, forceRemoveGuestOnExit = forceRemoveGuestOnExit, showDialog = this::showDialog, dismissDialog = this::dismissDialog, switchUser = this::switchUser, ) } private fun switchUser(userId: Int) { // TODO(b/246631653): track jank and lantecy like in the old impl. refreshUsersScheduler.pause() Loading @@ -533,7 +594,7 @@ constructor( dismissDialog() val selectedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1) if (previousUserInfo?.id != selectedUserId) { callbacks.forEach { it.onUserStateChanged() } notifyCallbacks() restartSecondaryService(selectedUserId) } if (guestUserInteractor.isGuestUserAutoCreated) { Loading packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt +15 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,21 @@ object LegacyUserDataHelper { ) } fun toUserActionModel(record: UserRecord): UserActionModel { check(!isUser(record)) return when { record.isAddUser -> UserActionModel.ADD_USER record.isAddSupervisedUser -> UserActionModel.ADD_SUPERVISED_USER record.isGuest -> UserActionModel.ENTER_GUEST_MODE else -> error("Not a known action: $record") } } fun isUser(record: UserRecord): Boolean { return record.info != null } private fun getEnforcedAdmin( context: Context, selectedUserId: Int, Loading packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt +59 −1 Original line number Diff line number Diff line Loading @@ -289,15 +289,73 @@ class GuestUserInteractorTest : SysuiTestCase() { verifyDidNotExit() } @Test fun `remove - returns to target user`() = runBlocking(IMMEDIATE) { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setSelectedUserInfo(GUEST_USER_INFO) val targetUserId = NON_GUEST_USER_INFO.id underTest.remove( guestUserId = GUEST_USER_INFO.id, targetUserId = targetUserId, showDialog = showDialog, dismissDialog = dismissDialog, switchUser = switchUser, ) verify(manager).markGuestForDeletion(GUEST_USER_INFO.id) verify(manager).removeUser(GUEST_USER_INFO.id) verify(switchUser).invoke(targetUserId) } @Test fun `remove - selected different from guest user - do nothing`() = runBlocking(IMMEDIATE) { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setSelectedUserInfo(NON_GUEST_USER_INFO) underTest.remove( guestUserId = GUEST_USER_INFO.id, targetUserId = 123, showDialog = showDialog, dismissDialog = dismissDialog, switchUser = switchUser, ) verifyDidNotRemove() } @Test fun `remove - selected is actually not a guest user - do nothing`() = runBlocking(IMMEDIATE) { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setSelectedUserInfo(NON_GUEST_USER_INFO) underTest.remove( guestUserId = NON_GUEST_USER_INFO.id, targetUserId = 123, showDialog = showDialog, dismissDialog = dismissDialog, switchUser = switchUser, ) verifyDidNotRemove() } private fun setAllowedToAdd(isAllowed: Boolean = true) { whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(isAllowed) whenever(devicePolicyManager.isDeviceManaged).thenReturn(!isAllowed) } private fun verifyDidNotExit() { verifyDidNotRemove() verify(manager, never()).getUserInfo(anyInt()) verify(manager, never()).markGuestForDeletion(anyInt()) verify(uiEventLogger, never()).log(any()) } private fun verifyDidNotRemove() { verify(manager, never()).markGuestForDeletion(anyInt()) verify(showDialog, never()).invoke(any()) verify(dismissDialog, never()).invoke() verify(switchUser, never()).invoke(anyInt()) Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt +82 −31 Original line number Diff line number Diff line Loading @@ -17,12 +17,21 @@ package com.android.systemui.statusbar.policy import android.content.Context import android.content.Intent import android.view.View import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.user.UserSwitchDialogController import com.android.systemui.user.data.source.UserRecord import com.android.systemui.user.domain.interactor.GuestUserInteractor import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.user.legacyhelper.data.LegacyUserDataHelper import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper import dagger.Lazy import java.io.PrintWriter import java.lang.ref.WeakReference Loading @@ -30,11 +39,17 @@ import javax.inject.Inject import kotlinx.coroutines.flow.Flow /** Implementation of [UserSwitcherController]. */ @SysUISingleton class UserSwitcherControllerImpl @Inject constructor( private val flags: FeatureFlags, @Application private val applicationContext: Context, flags: FeatureFlags, @Suppress("DEPRECATION") private val oldImpl: Lazy<UserSwitcherControllerOldImpl>, private val userInteractorLazy: Lazy<UserInteractor>, private val guestUserInteractorLazy: Lazy<GuestUserInteractor>, private val keyguardInteractorLazy: Lazy<KeyguardInteractor>, private val activityStarter: ActivityStarter, ) : UserSwitcherController { private val useInteractor: Boolean = Loading @@ -42,15 +57,21 @@ constructor( !flags.isEnabled(Flags.USER_INTERACTOR_AND_REPO_USE_CONTROLLER) private val _oldImpl: UserSwitcherControllerOldImpl get() = oldImpl.get() private val userInteractor: UserInteractor by lazy { userInteractorLazy.get() } private val guestUserInteractor: GuestUserInteractor by lazy { guestUserInteractorLazy.get() } private val keyguardInteractor: KeyguardInteractor by lazy { keyguardInteractorLazy.get() } private fun notYetImplemented(): Nothing { error("Not yet implemented!") private val callbackCompatMap = mutableMapOf<UserSwitcherController.UserSwitchCallback, UserInteractor.UserCallback>() private fun notSupported(): Nothing { error("Not supported in the new implementation!") } override val users: ArrayList<UserRecord> get() = if (useInteractor) { notYetImplemented() userInteractor.userRecords.value } else { _oldImpl.users } Loading @@ -58,15 +79,13 @@ constructor( override val isSimpleUserSwitcher: Boolean get() = if (useInteractor) { notYetImplemented() userInteractor.isSimpleUserSwitcher } else { _oldImpl.isSimpleUserSwitcher } override fun init(view: View) { if (useInteractor) { notYetImplemented() } else { if (!useInteractor) { _oldImpl.init(view) } } Loading @@ -74,7 +93,7 @@ constructor( override val currentUserRecord: UserRecord? get() = if (useInteractor) { notYetImplemented() userInteractor.selectedUserRecord.value } else { _oldImpl.currentUserRecord } Loading @@ -82,7 +101,14 @@ constructor( override val currentUserName: String? get() = if (useInteractor) { notYetImplemented() currentUserRecord?.let { LegacyUserUiHelper.getUserRecordName( context = applicationContext, record = it, isGuestUserAutoCreated = userInteractor.isGuestUserAutoCreated, isGuestUserResetting = userInteractor.isGuestUserResetting, ) } } else { _oldImpl.currentUserName } Loading @@ -92,7 +118,7 @@ constructor( dialogShower: UserSwitchDialogController.DialogShower? ) { if (useInteractor) { notYetImplemented() userInteractor.selectUser(userId) } else { _oldImpl.onUserSelected(userId, dialogShower) } Loading @@ -101,7 +127,7 @@ constructor( override val isAddUsersFromLockScreenEnabled: Flow<Boolean> get() = if (useInteractor) { notYetImplemented() notSupported() } else { _oldImpl.isAddUsersFromLockScreenEnabled } Loading @@ -109,7 +135,7 @@ constructor( override val isGuestUserAutoCreated: Boolean get() = if (useInteractor) { notYetImplemented() userInteractor.isGuestUserAutoCreated } else { _oldImpl.isGuestUserAutoCreated } Loading @@ -117,7 +143,7 @@ constructor( override val isGuestUserResetting: Boolean get() = if (useInteractor) { notYetImplemented() userInteractor.isGuestUserResetting } else { _oldImpl.isGuestUserResetting } Loading @@ -126,7 +152,7 @@ constructor( dialogShower: UserSwitchDialogController.DialogShower?, ) { if (useInteractor) { notYetImplemented() notSupported() } else { _oldImpl.createAndSwitchToGuestUser(dialogShower) } Loading @@ -134,7 +160,7 @@ constructor( override fun showAddUserDialog(dialogShower: UserSwitchDialogController.DialogShower?) { if (useInteractor) { notYetImplemented() notSupported() } else { _oldImpl.showAddUserDialog(dialogShower) } Loading @@ -142,23 +168,31 @@ constructor( override fun startSupervisedUserActivity() { if (useInteractor) { notYetImplemented() notSupported() } else { _oldImpl.startSupervisedUserActivity() } } override fun onDensityOrFontScaleChanged() { if (useInteractor) { notYetImplemented() } else { if (!useInteractor) { _oldImpl.onDensityOrFontScaleChanged() } } override fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) { if (useInteractor) { notYetImplemented() userInteractor.addCallback( object : UserInteractor.UserCallback { override fun isEvictable(): Boolean { return adapter.get() == null } override fun onUserStateChanged() { adapter.get()?.notifyDataSetChanged() } } ) } else { _oldImpl.addAdapter(adapter) } Loading @@ -169,7 +203,11 @@ constructor( dialogShower: UserSwitchDialogController.DialogShower?, ) { if (useInteractor) { notYetImplemented() if (LegacyUserDataHelper.isUser(record)) { userInteractor.selectUser(record.resolveId()) } else { userInteractor.executeAction(LegacyUserDataHelper.toUserActionModel(record)) } } else { _oldImpl.onUserListItemClicked(record, dialogShower) } Loading @@ -177,7 +215,10 @@ constructor( override fun removeGuestUser(guestUserId: Int, targetUserId: Int) { if (useInteractor) { notYetImplemented() userInteractor.removeGuestUser( guestUserId = guestUserId, targetUserId = targetUserId, ) } else { _oldImpl.removeGuestUser(guestUserId, targetUserId) } Loading @@ -189,7 +230,7 @@ constructor( forceRemoveGuestOnExit: Boolean ) { if (useInteractor) { notYetImplemented() userInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit) } else { _oldImpl.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit) } Loading @@ -197,7 +238,7 @@ constructor( override fun schedulePostBootGuestCreation() { if (useInteractor) { notYetImplemented() guestUserInteractor.onDeviceBootCompleted() } else { _oldImpl.schedulePostBootGuestCreation() } Loading @@ -206,14 +247,14 @@ constructor( override val isKeyguardShowing: Boolean get() = if (useInteractor) { notYetImplemented() keyguardInteractor.isKeyguardShowing() } else { _oldImpl.isKeyguardShowing } override fun startActivity(intent: Intent) { if (useInteractor) { notYetImplemented() activityStarter.startActivity(intent, /* dismissShade= */ false) } else { _oldImpl.startActivity(intent) } Loading @@ -221,7 +262,7 @@ constructor( override fun refreshUsers(forcePictureLoadForId: Int) { if (useInteractor) { notYetImplemented() userInteractor.refreshUsers() } else { _oldImpl.refreshUsers(forcePictureLoadForId) } Loading @@ -229,7 +270,14 @@ constructor( override fun addUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) { if (useInteractor) { notYetImplemented() val interactorCallback = object : UserInteractor.UserCallback { override fun onUserStateChanged() { callback.onUserSwitched() } } callbackCompatMap[callback] = interactorCallback userInteractor.addCallback(interactorCallback) } else { _oldImpl.addUserSwitchCallback(callback) } Loading @@ -237,7 +285,10 @@ constructor( override fun removeUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) { if (useInteractor) { notYetImplemented() val interactorCallback = callbackCompatMap.remove(callback) if (interactorCallback != null) { userInteractor.removeCallback(interactorCallback) } } else { _oldImpl.removeUserSwitchCallback(callback) } Loading @@ -245,7 +296,7 @@ constructor( override fun dump(pw: PrintWriter, args: Array<out String>) { if (useInteractor) { notYetImplemented() userInteractor.dump(pw) } else { _oldImpl.dump(pw, args) } Loading
packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt +1 −1 Original line number Diff line number Diff line Loading @@ -173,7 +173,7 @@ constructor( } /** Removes the guest user from the device. */ private suspend fun remove( suspend fun remove( @UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int, showDialog: (ShowDialogRequestModel) -> Unit, Loading
packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt +105 −44 Original line number Diff line number Diff line Loading @@ -52,8 +52,7 @@ import com.android.systemui.user.legacyhelper.data.LegacyUserDataHelper import com.android.systemui.user.shared.model.UserActionModel import com.android.systemui.user.shared.model.UserModel import com.android.systemui.util.kotlin.pairwise import java.util.Collections import java.util.WeakHashMap import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope Loading @@ -70,6 +69,9 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext /** Encapsulates business logic to interact with user data and systems. */ Loading @@ -96,7 +98,9 @@ constructor( * Defines interface for classes that can be notified when the state of users on the device is * changed. */ fun interface UserCallback { interface UserCallback { /** Returns `true` if this callback can be cleaned-up. */ fun isEvictable(): Boolean = false /** Notifies that the state of users on the device has changed. */ fun onUserStateChanged() } Loading @@ -110,7 +114,8 @@ constructor( com.android.internal.R.string.config_supervisedUserCreationPackage ) private val callbacks = Collections.newSetFromMap(WeakHashMap<UserCallback, Boolean>()) private val callbackMutex = Mutex() private val callbacks = mutableSetOf<UserCallback>() /** List of current on-device users to select from. */ val users: Flow<List<UserModel>> Loading Loading @@ -263,6 +268,7 @@ constructor( } ) } .onEach { notifyCallbacks() } .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, Loading Loading @@ -307,30 +313,6 @@ constructor( error("Not supported in the old implementation!") } fun addCallback(callback: UserCallback) { callbacks.add(callback) } fun removeCallback(callback: UserCallback) { callbacks.remove(callback) } fun onDialogShown() { _dialogShowRequests.value = null } fun onDialogDismissed() { _dialogDismissRequests.value = null } private fun showDialog(request: ShowDialogRequestModel) { _dialogShowRequests.value = request } private fun dismissDialog() { _dialogDismissRequests.value = Unit } init { if (isNewImpl) { refreshUsersScheduler.refreshIfNotPaused() Loading Loading @@ -364,6 +346,46 @@ constructor( } } fun addCallback(callback: UserCallback) { applicationScope.launch { callbackMutex.withLock { callbacks.add(callback) } } } fun removeCallback(callback: UserCallback) { applicationScope.launch { callbackMutex.withLock { callbacks.remove(callback) } } } fun refreshUsers() { refreshUsersScheduler.refreshIfNotPaused() } fun onDialogShown() { _dialogShowRequests.value = null } fun onDialogDismissed() { _dialogDismissRequests.value = null } fun dump(pw: PrintWriter) { pw.println("UserInteractor state:") pw.println(" lastSelectedNonGuestUserId=${repository.lastSelectedNonGuestUserId}") val users = userRecords.value.filter { it.info != null } pw.println(" userCount=${userRecords.value.count { LegacyUserDataHelper.isUser(it) }}") for (i in users.indices) { pw.println(" ${users[i]}") } val actions = userRecords.value.filter { it.info == null } pw.println(" actionCount=${userRecords.value.count { !LegacyUserDataHelper.isUser(it) }}") for (i in actions.indices) { pw.println(" ${actions[i]}") } pw.println("isSimpleUserSwitcher=$isSimpleUserSwitcher") pw.println("isGuestUserAutoCreated=$isGuestUserAutoCreated") } fun onDeviceBootCompleted() { guestUserInteractor.onDeviceBootCompleted() } Loading Loading @@ -460,6 +482,60 @@ constructor( } } fun exitGuestUser( @UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int, forceRemoveGuestOnExit: Boolean, ) { guestUserInteractor.exit( guestUserId = guestUserId, targetUserId = targetUserId, forceRemoveGuestOnExit = forceRemoveGuestOnExit, showDialog = this::showDialog, dismissDialog = this::dismissDialog, switchUser = this::switchUser, ) } fun removeGuestUser( @UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int, ) { applicationScope.launch { guestUserInteractor.remove( guestUserId = guestUserId, targetUserId = targetUserId, ::showDialog, ::dismissDialog, ::selectUser, ) } } private fun showDialog(request: ShowDialogRequestModel) { _dialogShowRequests.value = request } private fun dismissDialog() { _dialogDismissRequests.value = Unit } private fun notifyCallbacks() { applicationScope.launch { callbackMutex.withLock { val iterator = callbacks.iterator() while (iterator.hasNext()) { val callback = iterator.next() if (!callback.isEvictable()) { callback.onUserStateChanged() } else { iterator.remove() } } } } } private suspend fun toRecord( userInfo: UserInfo, selectedUserId: Int, Loading Loading @@ -498,21 +574,6 @@ constructor( ) } private fun exitGuestUser( @UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int, forceRemoveGuestOnExit: Boolean, ) { guestUserInteractor.exit( guestUserId = guestUserId, targetUserId = targetUserId, forceRemoveGuestOnExit = forceRemoveGuestOnExit, showDialog = this::showDialog, dismissDialog = this::dismissDialog, switchUser = this::switchUser, ) } private fun switchUser(userId: Int) { // TODO(b/246631653): track jank and lantecy like in the old impl. refreshUsersScheduler.pause() Loading @@ -533,7 +594,7 @@ constructor( dismissDialog() val selectedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1) if (previousUserInfo?.id != selectedUserId) { callbacks.forEach { it.onUserStateChanged() } notifyCallbacks() restartSecondaryService(selectedUserId) } if (guestUserInteractor.isGuestUserAutoCreated) { Loading
packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt +15 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,21 @@ object LegacyUserDataHelper { ) } fun toUserActionModel(record: UserRecord): UserActionModel { check(!isUser(record)) return when { record.isAddUser -> UserActionModel.ADD_USER record.isAddSupervisedUser -> UserActionModel.ADD_SUPERVISED_USER record.isGuest -> UserActionModel.ENTER_GUEST_MODE else -> error("Not a known action: $record") } } fun isUser(record: UserRecord): Boolean { return record.info != null } private fun getEnforcedAdmin( context: Context, selectedUserId: Int, Loading
packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt +59 −1 Original line number Diff line number Diff line Loading @@ -289,15 +289,73 @@ class GuestUserInteractorTest : SysuiTestCase() { verifyDidNotExit() } @Test fun `remove - returns to target user`() = runBlocking(IMMEDIATE) { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setSelectedUserInfo(GUEST_USER_INFO) val targetUserId = NON_GUEST_USER_INFO.id underTest.remove( guestUserId = GUEST_USER_INFO.id, targetUserId = targetUserId, showDialog = showDialog, dismissDialog = dismissDialog, switchUser = switchUser, ) verify(manager).markGuestForDeletion(GUEST_USER_INFO.id) verify(manager).removeUser(GUEST_USER_INFO.id) verify(switchUser).invoke(targetUserId) } @Test fun `remove - selected different from guest user - do nothing`() = runBlocking(IMMEDIATE) { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setSelectedUserInfo(NON_GUEST_USER_INFO) underTest.remove( guestUserId = GUEST_USER_INFO.id, targetUserId = 123, showDialog = showDialog, dismissDialog = dismissDialog, switchUser = switchUser, ) verifyDidNotRemove() } @Test fun `remove - selected is actually not a guest user - do nothing`() = runBlocking(IMMEDIATE) { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setSelectedUserInfo(NON_GUEST_USER_INFO) underTest.remove( guestUserId = NON_GUEST_USER_INFO.id, targetUserId = 123, showDialog = showDialog, dismissDialog = dismissDialog, switchUser = switchUser, ) verifyDidNotRemove() } private fun setAllowedToAdd(isAllowed: Boolean = true) { whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(isAllowed) whenever(devicePolicyManager.isDeviceManaged).thenReturn(!isAllowed) } private fun verifyDidNotExit() { verifyDidNotRemove() verify(manager, never()).getUserInfo(anyInt()) verify(manager, never()).markGuestForDeletion(anyInt()) verify(uiEventLogger, never()).log(any()) } private fun verifyDidNotRemove() { verify(manager, never()).markGuestForDeletion(anyInt()) verify(showDialog, never()).invoke(any()) verify(dismissDialog, never()).invoke() verify(switchUser, never()).invoke(anyInt()) Loading