User data layer without dependency on USC
Behind a flag, removes the dependency on UserSwitcherController from the data layer of the user feature. Bug: 246631653 Test: included tests, manually verified against the tip of the CL chain Change-Id: I0594dcfd5cc38d0ae09f620e01d0caafc1b956e3
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.systemui.user.data.model
|
||||
|
||||
/** Encapsulates the state of settings related to user switching. */
|
||||
data class UserSwitcherSettingsModel(
|
||||
val isSimpleUserSwitcher: Boolean = false,
|
||||
val isAddUsersFromLockscreen: Boolean = false,
|
||||
val isUserSwitcherEnabled: Boolean = false,
|
||||
)
|
||||
@@ -18,9 +18,13 @@
|
||||
package com.android.systemui.user.data.repository
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.UserInfo
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.provider.Settings
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import com.android.internal.util.UserIcons
|
||||
import com.android.systemui.R
|
||||
@@ -29,15 +33,36 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
|
||||
import com.android.systemui.common.shared.model.Text
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
||||
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
||||
import com.android.systemui.user.data.source.UserRecord
|
||||
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
|
||||
import com.android.systemui.user.shared.model.UserActionModel
|
||||
import com.android.systemui.user.shared.model.UserModel
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.asExecutor
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* Acts as source of truth for user related data.
|
||||
@@ -55,6 +80,18 @@ interface UserRepository {
|
||||
/** List of available user-related actions. */
|
||||
val actions: Flow<List<UserActionModel>>
|
||||
|
||||
/** User switcher related settings. */
|
||||
val userSwitcherSettings: Flow<UserSwitcherSettingsModel>
|
||||
|
||||
/** List of all users on the device. */
|
||||
val userInfos: Flow<List<UserInfo>>
|
||||
|
||||
/** [UserInfo] of the currently-selected user. */
|
||||
val selectedUserInfo: Flow<UserInfo>
|
||||
|
||||
/** User ID of the last non-guest selected user. */
|
||||
val lastSelectedNonGuestUserId: Int
|
||||
|
||||
/** Whether actions are available even when locked. */
|
||||
val isActionableWhenLocked: Flow<Boolean>
|
||||
|
||||
@@ -62,7 +99,23 @@ interface UserRepository {
|
||||
val isGuestUserAutoCreated: Boolean
|
||||
|
||||
/** Whether the guest user is currently being reset. */
|
||||
val isGuestUserResetting: Boolean
|
||||
var isGuestUserResetting: Boolean
|
||||
|
||||
/** Whether we've scheduled the creation of a guest user. */
|
||||
val isGuestUserCreationScheduled: AtomicBoolean
|
||||
|
||||
/** The user of the secondary service. */
|
||||
var secondaryUserId: Int
|
||||
|
||||
/** Whether refresh users should be paused. */
|
||||
var isRefreshUsersPaused: Boolean
|
||||
|
||||
/** Asynchronously refresh the list of users. This will cause [userInfos] to be updated. */
|
||||
fun refreshUsers()
|
||||
|
||||
fun getSelectedUserInfo(): UserInfo
|
||||
|
||||
fun isSimpleUserSwitcher(): Boolean
|
||||
}
|
||||
|
||||
@SysUISingleton
|
||||
@@ -71,9 +124,31 @@ class UserRepositoryImpl
|
||||
constructor(
|
||||
@Application private val appContext: Context,
|
||||
private val manager: UserManager,
|
||||
controller: UserSwitcherController,
|
||||
private val controller: UserSwitcherController,
|
||||
@Application private val applicationScope: CoroutineScope,
|
||||
@Main private val mainDispatcher: CoroutineDispatcher,
|
||||
@Background private val backgroundDispatcher: CoroutineDispatcher,
|
||||
private val globalSettings: GlobalSettings,
|
||||
private val tracker: UserTracker,
|
||||
private val featureFlags: FeatureFlags,
|
||||
) : UserRepository {
|
||||
|
||||
private val isNewImpl: Boolean
|
||||
get() = featureFlags.isEnabled(Flags.REFACTORED_USER_SWITCHER_CONTROLLER)
|
||||
|
||||
private val _userSwitcherSettings = MutableStateFlow<UserSwitcherSettingsModel?>(null)
|
||||
override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> =
|
||||
_userSwitcherSettings.asStateFlow().filterNotNull()
|
||||
|
||||
private val _userInfos = MutableStateFlow<List<UserInfo>?>(null)
|
||||
override val userInfos: Flow<List<UserInfo>> = _userInfos.filterNotNull()
|
||||
|
||||
private val _selectedUserInfo = MutableStateFlow<UserInfo?>(null)
|
||||
override val selectedUserInfo: Flow<UserInfo> = _selectedUserInfo.filterNotNull()
|
||||
|
||||
override var lastSelectedNonGuestUserId: Int = UserHandle.USER_SYSTEM
|
||||
private set
|
||||
|
||||
private val userRecords: Flow<List<UserRecord>> = conflatedCallbackFlow {
|
||||
fun send() {
|
||||
trySendWithFailureLogging(
|
||||
@@ -99,11 +174,148 @@ constructor(
|
||||
override val actions: Flow<List<UserActionModel>> =
|
||||
userRecords.map { records -> records.filter { it.isNotUser() }.map { it.toActionModel() } }
|
||||
|
||||
override val isActionableWhenLocked: Flow<Boolean> = controller.isAddUsersFromLockScreenEnabled
|
||||
override val isActionableWhenLocked: Flow<Boolean> =
|
||||
if (isNewImpl) {
|
||||
emptyFlow()
|
||||
} else {
|
||||
controller.isAddUsersFromLockScreenEnabled
|
||||
}
|
||||
|
||||
override val isGuestUserAutoCreated: Boolean = controller.isGuestUserAutoCreated
|
||||
override val isGuestUserAutoCreated: Boolean =
|
||||
if (isNewImpl) {
|
||||
appContext.resources.getBoolean(com.android.internal.R.bool.config_guestUserAutoCreated)
|
||||
} else {
|
||||
controller.isGuestUserAutoCreated
|
||||
}
|
||||
|
||||
override val isGuestUserResetting: Boolean = controller.isGuestUserResetting
|
||||
private var _isGuestUserResetting: Boolean = false
|
||||
override var isGuestUserResetting: Boolean =
|
||||
if (isNewImpl) {
|
||||
_isGuestUserResetting
|
||||
} else {
|
||||
controller.isGuestUserResetting
|
||||
}
|
||||
set(value) =
|
||||
if (isNewImpl) {
|
||||
_isGuestUserResetting = value
|
||||
} else {
|
||||
error("Not supported in the old implementation!")
|
||||
}
|
||||
|
||||
override val isGuestUserCreationScheduled = AtomicBoolean()
|
||||
|
||||
override var secondaryUserId: Int = UserHandle.USER_NULL
|
||||
|
||||
override var isRefreshUsersPaused: Boolean = false
|
||||
|
||||
init {
|
||||
if (isNewImpl) {
|
||||
observeSelectedUser()
|
||||
observeUserSettings()
|
||||
}
|
||||
}
|
||||
|
||||
override fun refreshUsers() {
|
||||
applicationScope.launch {
|
||||
val result = withContext(backgroundDispatcher) { manager.aliveUsers }
|
||||
|
||||
if (result != null) {
|
||||
_userInfos.value = result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectedUserInfo(): UserInfo {
|
||||
return checkNotNull(_selectedUserInfo.value)
|
||||
}
|
||||
|
||||
override fun isSimpleUserSwitcher(): Boolean {
|
||||
return checkNotNull(_userSwitcherSettings.value?.isSimpleUserSwitcher)
|
||||
}
|
||||
|
||||
private fun observeSelectedUser() {
|
||||
conflatedCallbackFlow {
|
||||
fun send() {
|
||||
trySendWithFailureLogging(tracker.userInfo, TAG)
|
||||
}
|
||||
|
||||
val callback =
|
||||
object : UserTracker.Callback {
|
||||
override fun onUserChanged(newUser: Int, userContext: Context) {
|
||||
send()
|
||||
}
|
||||
}
|
||||
|
||||
tracker.addCallback(callback, mainDispatcher.asExecutor())
|
||||
send()
|
||||
|
||||
awaitClose { tracker.removeCallback(callback) }
|
||||
}
|
||||
.onEach {
|
||||
if (!it.isGuest) {
|
||||
lastSelectedNonGuestUserId = it.id
|
||||
}
|
||||
|
||||
_selectedUserInfo.value = it
|
||||
}
|
||||
.launchIn(applicationScope)
|
||||
}
|
||||
|
||||
private fun observeUserSettings() {
|
||||
globalSettings
|
||||
.observerFlow(
|
||||
names =
|
||||
arrayOf(
|
||||
SETTING_SIMPLE_USER_SWITCHER,
|
||||
Settings.Global.ADD_USERS_WHEN_LOCKED,
|
||||
Settings.Global.USER_SWITCHER_ENABLED,
|
||||
),
|
||||
userId = UserHandle.USER_SYSTEM,
|
||||
)
|
||||
.onStart { emit(Unit) } // Forces an initial update.
|
||||
.map { getSettings() }
|
||||
.onEach { _userSwitcherSettings.value = it }
|
||||
.launchIn(applicationScope)
|
||||
}
|
||||
|
||||
private suspend fun getSettings(): UserSwitcherSettingsModel {
|
||||
return withContext(backgroundDispatcher) {
|
||||
val isSimpleUserSwitcher =
|
||||
globalSettings.getIntForUser(
|
||||
SETTING_SIMPLE_USER_SWITCHER,
|
||||
if (
|
||||
appContext.resources.getBoolean(
|
||||
com.android.internal.R.bool.config_expandLockScreenUserSwitcher
|
||||
)
|
||||
) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
UserHandle.USER_SYSTEM,
|
||||
) != 0
|
||||
|
||||
val isAddUsersFromLockscreen =
|
||||
globalSettings.getIntForUser(
|
||||
Settings.Global.ADD_USERS_WHEN_LOCKED,
|
||||
0,
|
||||
UserHandle.USER_SYSTEM,
|
||||
) != 0
|
||||
|
||||
val isUserSwitcherEnabled =
|
||||
globalSettings.getIntForUser(
|
||||
Settings.Global.USER_SWITCHER_ENABLED,
|
||||
0,
|
||||
UserHandle.USER_SYSTEM,
|
||||
) != 0
|
||||
|
||||
UserSwitcherSettingsModel(
|
||||
isSimpleUserSwitcher = isSimpleUserSwitcher,
|
||||
isAddUsersFromLockscreen = isAddUsersFromLockscreen,
|
||||
isUserSwitcherEnabled = isUserSwitcherEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun UserRecord.isUser(): Boolean {
|
||||
return when {
|
||||
@@ -125,6 +337,7 @@ constructor(
|
||||
image = getUserImage(this),
|
||||
isSelected = isCurrent,
|
||||
isSelectable = isSwitchToEnabled || isGuest,
|
||||
isGuest = isGuest,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -162,5 +375,6 @@ constructor(
|
||||
|
||||
companion object {
|
||||
private const val TAG = "UserRepository"
|
||||
@VisibleForTesting const val SETTING_SIMPLE_USER_SWITCHER = "lockscreenSimpleUserSwitcher"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,7 @@ data class UserModel(
|
||||
val isSelected: Boolean,
|
||||
/** Whether this use is selectable. A non-selectable user cannot be switched to. */
|
||||
val isSelectable: Boolean,
|
||||
/** Whether this model represents the guest user. */
|
||||
// TODO(b/246631653): remove this default value it was only here to be able to split up CLs
|
||||
val isGuest: Boolean = false,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.systemui.util.settings
|
||||
|
||||
import android.annotation.UserIdInt
|
||||
import android.database.ContentObserver
|
||||
import android.os.UserHandle
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/** Kotlin extension functions for [SettingsProxy]. */
|
||||
object SettingsProxyExt {
|
||||
|
||||
/** Returns a flow of [Unit] that is invoked each time that content is updated. */
|
||||
fun SettingsProxy.observerFlow(
|
||||
vararg names: String,
|
||||
@UserIdInt userId: Int = UserHandle.USER_CURRENT,
|
||||
): Flow<Unit> {
|
||||
return conflatedCallbackFlow {
|
||||
val observer =
|
||||
object : ContentObserver(null) {
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
trySend(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
names.forEach { name -> registerContentObserverForUser(name, observer, userId) }
|
||||
|
||||
awaitClose { unregisterContentObserver(observer) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.systemui.user.data.repository
|
||||
|
||||
import android.content.pm.UserInfo
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.provider.Settings
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class UserRepositoryImplRefactoredTest : UserRepositoryImplTest() {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
super.setUp(isRefactored = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun userSwitcherSettings() = runSelfCancelingTest {
|
||||
setUpGlobalSettings(
|
||||
isSimpleUserSwitcher = true,
|
||||
isAddUsersFromLockscreen = true,
|
||||
isUserSwitcherEnabled = true,
|
||||
)
|
||||
underTest = create(this)
|
||||
|
||||
var value: UserSwitcherSettingsModel? = null
|
||||
underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
|
||||
|
||||
assertUserSwitcherSettings(
|
||||
model = value,
|
||||
expectedSimpleUserSwitcher = true,
|
||||
expectedAddUsersFromLockscreen = true,
|
||||
expectedUserSwitcherEnabled = true,
|
||||
)
|
||||
|
||||
setUpGlobalSettings(
|
||||
isSimpleUserSwitcher = false,
|
||||
isAddUsersFromLockscreen = true,
|
||||
isUserSwitcherEnabled = true,
|
||||
)
|
||||
assertUserSwitcherSettings(
|
||||
model = value,
|
||||
expectedSimpleUserSwitcher = false,
|
||||
expectedAddUsersFromLockscreen = true,
|
||||
expectedUserSwitcherEnabled = true,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshUsers() = runSelfCancelingTest {
|
||||
underTest = create(this)
|
||||
val initialExpectedValue =
|
||||
setUpUsers(
|
||||
count = 3,
|
||||
selectedIndex = 0,
|
||||
)
|
||||
var userInfos: List<UserInfo>? = null
|
||||
var selectedUserInfo: UserInfo? = null
|
||||
underTest.userInfos.onEach { userInfos = it }.launchIn(this)
|
||||
underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
|
||||
|
||||
underTest.refreshUsers()
|
||||
assertThat(userInfos).isEqualTo(initialExpectedValue)
|
||||
assertThat(selectedUserInfo).isEqualTo(initialExpectedValue[0])
|
||||
assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
|
||||
|
||||
val secondExpectedValue =
|
||||
setUpUsers(
|
||||
count = 4,
|
||||
selectedIndex = 1,
|
||||
)
|
||||
underTest.refreshUsers()
|
||||
assertThat(userInfos).isEqualTo(secondExpectedValue)
|
||||
assertThat(selectedUserInfo).isEqualTo(secondExpectedValue[1])
|
||||
assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
|
||||
|
||||
val selectedNonGuestUserId = selectedUserInfo?.id
|
||||
val thirdExpectedValue =
|
||||
setUpUsers(
|
||||
count = 2,
|
||||
hasGuest = true,
|
||||
selectedIndex = 1,
|
||||
)
|
||||
underTest.refreshUsers()
|
||||
assertThat(userInfos).isEqualTo(thirdExpectedValue)
|
||||
assertThat(selectedUserInfo).isEqualTo(thirdExpectedValue[1])
|
||||
assertThat(selectedUserInfo?.isGuest).isTrue()
|
||||
assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedNonGuestUserId)
|
||||
}
|
||||
|
||||
private fun setUpUsers(
|
||||
count: Int,
|
||||
hasGuest: Boolean = false,
|
||||
selectedIndex: Int = 0,
|
||||
): List<UserInfo> {
|
||||
val userInfos =
|
||||
(0 until count).map { index ->
|
||||
createUserInfo(
|
||||
index,
|
||||
isGuest = hasGuest && index == count - 1,
|
||||
)
|
||||
}
|
||||
whenever(manager.aliveUsers).thenReturn(userInfos)
|
||||
tracker.set(userInfos, selectedIndex)
|
||||
return userInfos
|
||||
}
|
||||
|
||||
private fun createUserInfo(
|
||||
id: Int,
|
||||
isGuest: Boolean,
|
||||
): UserInfo {
|
||||
val flags = 0
|
||||
return UserInfo(
|
||||
id,
|
||||
"user_$id",
|
||||
/* iconPath= */ "",
|
||||
flags,
|
||||
if (isGuest) UserManager.USER_TYPE_FULL_GUEST else UserInfo.getDefaultUserType(flags),
|
||||
)
|
||||
}
|
||||
|
||||
private fun setUpGlobalSettings(
|
||||
isSimpleUserSwitcher: Boolean = false,
|
||||
isAddUsersFromLockscreen: Boolean = false,
|
||||
isUserSwitcherEnabled: Boolean = true,
|
||||
) {
|
||||
context.orCreateTestableResources.addOverride(
|
||||
com.android.internal.R.bool.config_expandLockScreenUserSwitcher,
|
||||
true,
|
||||
)
|
||||
globalSettings.putIntForUser(
|
||||
UserRepositoryImpl.SETTING_SIMPLE_USER_SWITCHER,
|
||||
if (isSimpleUserSwitcher) 1 else 0,
|
||||
UserHandle.USER_SYSTEM,
|
||||
)
|
||||
globalSettings.putIntForUser(
|
||||
Settings.Global.ADD_USERS_WHEN_LOCKED,
|
||||
if (isAddUsersFromLockscreen) 1 else 0,
|
||||
UserHandle.USER_SYSTEM,
|
||||
)
|
||||
globalSettings.putIntForUser(
|
||||
Settings.Global.USER_SWITCHER_ENABLED,
|
||||
if (isUserSwitcherEnabled) 1 else 0,
|
||||
UserHandle.USER_SYSTEM,
|
||||
)
|
||||
}
|
||||
|
||||
private fun assertUserSwitcherSettings(
|
||||
model: UserSwitcherSettingsModel?,
|
||||
expectedSimpleUserSwitcher: Boolean,
|
||||
expectedAddUsersFromLockscreen: Boolean,
|
||||
expectedUserSwitcherEnabled: Boolean,
|
||||
) {
|
||||
checkNotNull(model)
|
||||
assertThat(model.isSimpleUserSwitcher).isEqualTo(expectedSimpleUserSwitcher)
|
||||
assertThat(model.isAddUsersFromLockscreen).isEqualTo(expectedAddUsersFromLockscreen)
|
||||
assertThat(model.isUserSwitcherEnabled).isEqualTo(expectedUserSwitcherEnabled)
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given block of execution within the scope of a dedicated [CoroutineScope] which
|
||||
* is then automatically canceled and cleaned-up.
|
||||
*/
|
||||
private fun runSelfCancelingTest(
|
||||
block: suspend CoroutineScope.() -> Unit,
|
||||
) =
|
||||
runBlocking(Dispatchers.Main.immediate) {
|
||||
val scope = CoroutineScope(coroutineContext + Job())
|
||||
block(scope)
|
||||
scope.cancel()
|
||||
}
|
||||
}
|
||||
@@ -17,201 +17,54 @@
|
||||
|
||||
package com.android.systemui.user.data.repository
|
||||
|
||||
import android.content.pm.UserInfo
|
||||
import android.os.UserManager
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.settings.FakeUserTracker
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
||||
import com.android.systemui.user.data.source.UserRecord
|
||||
import com.android.systemui.user.shared.model.UserActionModel
|
||||
import com.android.systemui.user.shared.model.UserModel
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.capture
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.android.systemui.util.settings.FakeSettings
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Captor
|
||||
import kotlinx.coroutines.test.TestCoroutineScope
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class UserRepositoryImplTest : SysuiTestCase() {
|
||||
abstract class UserRepositoryImplTest : SysuiTestCase() {
|
||||
|
||||
@Mock private lateinit var manager: UserManager
|
||||
@Mock private lateinit var controller: UserSwitcherController
|
||||
@Captor
|
||||
private lateinit var userSwitchCallbackCaptor:
|
||||
ArgumentCaptor<UserSwitcherController.UserSwitchCallback>
|
||||
@Mock protected lateinit var manager: UserManager
|
||||
@Mock protected lateinit var controller: UserSwitcherController
|
||||
|
||||
private lateinit var underTest: UserRepositoryImpl
|
||||
protected lateinit var underTest: UserRepositoryImpl
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
protected lateinit var globalSettings: FakeSettings
|
||||
protected lateinit var tracker: FakeUserTracker
|
||||
protected lateinit var featureFlags: FakeFeatureFlags
|
||||
|
||||
protected fun setUp(isRefactored: Boolean) {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
whenever(controller.isAddUsersFromLockScreenEnabled).thenReturn(MutableStateFlow(false))
|
||||
whenever(controller.isGuestUserAutoCreated).thenReturn(false)
|
||||
whenever(controller.isGuestUserResetting).thenReturn(false)
|
||||
|
||||
underTest =
|
||||
UserRepositoryImpl(
|
||||
appContext = context,
|
||||
manager = manager,
|
||||
controller = controller,
|
||||
)
|
||||
globalSettings = FakeSettings()
|
||||
tracker = FakeUserTracker()
|
||||
featureFlags = FakeFeatureFlags()
|
||||
featureFlags.set(Flags.REFACTORED_USER_SWITCHER_CONTROLLER, isRefactored)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users - registers for updates`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.users.onEach {}.launchIn(this)
|
||||
|
||||
verify(controller).addUserSwitchCallback(any())
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users - unregisters from updates`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.users.onEach {}.launchIn(this)
|
||||
verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
|
||||
|
||||
job.cancel()
|
||||
|
||||
verify(controller).removeUserSwitchCallback(userSwitchCallbackCaptor.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users - does not include actions`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
whenever(controller.users)
|
||||
.thenReturn(
|
||||
arrayListOf(
|
||||
createUserRecord(0, isSelected = true),
|
||||
createActionRecord(UserActionModel.ADD_USER),
|
||||
createUserRecord(1),
|
||||
createUserRecord(2),
|
||||
createActionRecord(UserActionModel.ADD_SUPERVISED_USER),
|
||||
createActionRecord(UserActionModel.ENTER_GUEST_MODE),
|
||||
)
|
||||
)
|
||||
var models: List<UserModel>? = null
|
||||
val job = underTest.users.onEach { models = it }.launchIn(this)
|
||||
|
||||
assertThat(models).hasSize(3)
|
||||
assertThat(models?.get(0)?.id).isEqualTo(0)
|
||||
assertThat(models?.get(0)?.isSelected).isTrue()
|
||||
assertThat(models?.get(1)?.id).isEqualTo(1)
|
||||
assertThat(models?.get(1)?.isSelected).isFalse()
|
||||
assertThat(models?.get(2)?.id).isEqualTo(2)
|
||||
assertThat(models?.get(2)?.isSelected).isFalse()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun selectedUser() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
whenever(controller.users)
|
||||
.thenReturn(
|
||||
arrayListOf(
|
||||
createUserRecord(0, isSelected = true),
|
||||
createUserRecord(1),
|
||||
createUserRecord(2),
|
||||
)
|
||||
)
|
||||
var id: Int? = null
|
||||
val job = underTest.selectedUser.map { it.id }.onEach { id = it }.launchIn(this)
|
||||
|
||||
assertThat(id).isEqualTo(0)
|
||||
|
||||
whenever(controller.users)
|
||||
.thenReturn(
|
||||
arrayListOf(
|
||||
createUserRecord(0),
|
||||
createUserRecord(1),
|
||||
createUserRecord(2, isSelected = true),
|
||||
)
|
||||
)
|
||||
verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
|
||||
userSwitchCallbackCaptor.value.onUserSwitched()
|
||||
assertThat(id).isEqualTo(2)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actions - unregisters from updates`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.actions.onEach {}.launchIn(this)
|
||||
verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
|
||||
|
||||
job.cancel()
|
||||
|
||||
verify(controller).removeUserSwitchCallback(userSwitchCallbackCaptor.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actions - registers for updates`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.actions.onEach {}.launchIn(this)
|
||||
|
||||
verify(controller).addUserSwitchCallback(any())
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actopms - does not include users`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
whenever(controller.users)
|
||||
.thenReturn(
|
||||
arrayListOf(
|
||||
createUserRecord(0, isSelected = true),
|
||||
createActionRecord(UserActionModel.ADD_USER),
|
||||
createUserRecord(1),
|
||||
createUserRecord(2),
|
||||
createActionRecord(UserActionModel.ADD_SUPERVISED_USER),
|
||||
createActionRecord(UserActionModel.ENTER_GUEST_MODE),
|
||||
)
|
||||
)
|
||||
var models: List<UserActionModel>? = null
|
||||
val job = underTest.actions.onEach { models = it }.launchIn(this)
|
||||
|
||||
assertThat(models).hasSize(3)
|
||||
assertThat(models?.get(0)).isEqualTo(UserActionModel.ADD_USER)
|
||||
assertThat(models?.get(1)).isEqualTo(UserActionModel.ADD_SUPERVISED_USER)
|
||||
assertThat(models?.get(2)).isEqualTo(UserActionModel.ENTER_GUEST_MODE)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
private fun createUserRecord(id: Int, isSelected: Boolean = false): UserRecord {
|
||||
return UserRecord(
|
||||
info = UserInfo(id, "name$id", 0),
|
||||
isCurrent = isSelected,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createActionRecord(action: UserActionModel): UserRecord {
|
||||
return UserRecord(
|
||||
isAddUser = action == UserActionModel.ADD_USER,
|
||||
isAddSupervisedUser = action == UserActionModel.ADD_SUPERVISED_USER,
|
||||
isGuest = action == UserActionModel.ENTER_GUEST_MODE,
|
||||
protected fun create(scope: CoroutineScope = TestCoroutineScope()): UserRepositoryImpl {
|
||||
return UserRepositoryImpl(
|
||||
appContext = context,
|
||||
manager = manager,
|
||||
controller = controller,
|
||||
applicationScope = scope,
|
||||
mainDispatcher = IMMEDIATE,
|
||||
backgroundDispatcher = IMMEDIATE,
|
||||
globalSettings = globalSettings,
|
||||
tracker = tracker,
|
||||
featureFlags = featureFlags,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
@JvmStatic protected val IMMEDIATE = Dispatchers.Main.immediate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.systemui.user.data.repository
|
||||
|
||||
import android.content.pm.UserInfo
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
||||
import com.android.systemui.user.data.source.UserRecord
|
||||
import com.android.systemui.user.shared.model.UserActionModel
|
||||
import com.android.systemui.user.shared.model.UserModel
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.capture
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class UserRepositoryImplUnrefactoredTest : UserRepositoryImplTest() {
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
}
|
||||
|
||||
@Captor
|
||||
private lateinit var userSwitchCallbackCaptor:
|
||||
ArgumentCaptor<UserSwitcherController.UserSwitchCallback>
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
super.setUp(isRefactored = false)
|
||||
|
||||
whenever(controller.isAddUsersFromLockScreenEnabled).thenReturn(MutableStateFlow(false))
|
||||
whenever(controller.isGuestUserAutoCreated).thenReturn(false)
|
||||
whenever(controller.isGuestUserResetting).thenReturn(false)
|
||||
|
||||
underTest = create()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users - registers for updates`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.users.onEach {}.launchIn(this)
|
||||
|
||||
verify(controller).addUserSwitchCallback(any())
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users - unregisters from updates`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.users.onEach {}.launchIn(this)
|
||||
verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
|
||||
|
||||
job.cancel()
|
||||
|
||||
verify(controller).removeUserSwitchCallback(userSwitchCallbackCaptor.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `users - does not include actions`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
whenever(controller.users)
|
||||
.thenReturn(
|
||||
arrayListOf(
|
||||
createUserRecord(0, isSelected = true),
|
||||
createActionRecord(UserActionModel.ADD_USER),
|
||||
createUserRecord(1),
|
||||
createUserRecord(2),
|
||||
createActionRecord(UserActionModel.ADD_SUPERVISED_USER),
|
||||
createActionRecord(UserActionModel.ENTER_GUEST_MODE),
|
||||
)
|
||||
)
|
||||
var models: List<UserModel>? = null
|
||||
val job = underTest.users.onEach { models = it }.launchIn(this)
|
||||
|
||||
assertThat(models).hasSize(3)
|
||||
assertThat(models?.get(0)?.id).isEqualTo(0)
|
||||
assertThat(models?.get(0)?.isSelected).isTrue()
|
||||
assertThat(models?.get(1)?.id).isEqualTo(1)
|
||||
assertThat(models?.get(1)?.isSelected).isFalse()
|
||||
assertThat(models?.get(2)?.id).isEqualTo(2)
|
||||
assertThat(models?.get(2)?.isSelected).isFalse()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun selectedUser() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
whenever(controller.users)
|
||||
.thenReturn(
|
||||
arrayListOf(
|
||||
createUserRecord(0, isSelected = true),
|
||||
createUserRecord(1),
|
||||
createUserRecord(2),
|
||||
)
|
||||
)
|
||||
var id: Int? = null
|
||||
val job = underTest.selectedUser.map { it.id }.onEach { id = it }.launchIn(this)
|
||||
|
||||
assertThat(id).isEqualTo(0)
|
||||
|
||||
whenever(controller.users)
|
||||
.thenReturn(
|
||||
arrayListOf(
|
||||
createUserRecord(0),
|
||||
createUserRecord(1),
|
||||
createUserRecord(2, isSelected = true),
|
||||
)
|
||||
)
|
||||
verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
|
||||
userSwitchCallbackCaptor.value.onUserSwitched()
|
||||
assertThat(id).isEqualTo(2)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actions - unregisters from updates`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.actions.onEach {}.launchIn(this)
|
||||
verify(controller).addUserSwitchCallback(capture(userSwitchCallbackCaptor))
|
||||
|
||||
job.cancel()
|
||||
|
||||
verify(controller).removeUserSwitchCallback(userSwitchCallbackCaptor.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actions - registers for updates`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.actions.onEach {}.launchIn(this)
|
||||
|
||||
verify(controller).addUserSwitchCallback(any())
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actions - does not include users`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
whenever(controller.users)
|
||||
.thenReturn(
|
||||
arrayListOf(
|
||||
createUserRecord(0, isSelected = true),
|
||||
createActionRecord(UserActionModel.ADD_USER),
|
||||
createUserRecord(1),
|
||||
createUserRecord(2),
|
||||
createActionRecord(UserActionModel.ADD_SUPERVISED_USER),
|
||||
createActionRecord(UserActionModel.ENTER_GUEST_MODE),
|
||||
)
|
||||
)
|
||||
var models: List<UserActionModel>? = null
|
||||
val job = underTest.actions.onEach { models = it }.launchIn(this)
|
||||
|
||||
assertThat(models).hasSize(3)
|
||||
assertThat(models?.get(0)).isEqualTo(UserActionModel.ADD_USER)
|
||||
assertThat(models?.get(1)).isEqualTo(UserActionModel.ADD_SUPERVISED_USER)
|
||||
assertThat(models?.get(2)).isEqualTo(UserActionModel.ENTER_GUEST_MODE)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
private fun createUserRecord(id: Int, isSelected: Boolean = false): UserRecord {
|
||||
return UserRecord(
|
||||
info = UserInfo(id, "name$id", 0),
|
||||
isCurrent = isSelected,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createActionRecord(action: UserActionModel): UserRecord {
|
||||
return UserRecord(
|
||||
isAddUser = action == UserActionModel.ADD_USER,
|
||||
isAddSupervisedUser = action == UserActionModel.ADD_SUPERVISED_USER,
|
||||
isGuest = action == UserActionModel.ENTER_GUEST_MODE,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -26,20 +26,24 @@ import java.util.concurrent.Executor
|
||||
|
||||
/** A fake [UserTracker] to be used in tests. */
|
||||
class FakeUserTracker(
|
||||
userId: Int = 0,
|
||||
userHandle: UserHandle = UserHandle.of(userId),
|
||||
userInfo: UserInfo = mock(),
|
||||
userProfiles: List<UserInfo> = emptyList(),
|
||||
private var _userId: Int = 0,
|
||||
private var _userHandle: UserHandle = UserHandle.of(_userId),
|
||||
private var _userInfo: UserInfo = mock(),
|
||||
private var _userProfiles: List<UserInfo> = emptyList(),
|
||||
userContentResolver: ContentResolver = MockContentResolver(),
|
||||
userContext: Context = mock(),
|
||||
private val onCreateCurrentUserContext: (Context) -> Context = { mock() },
|
||||
) : UserTracker {
|
||||
val callbacks = mutableListOf<UserTracker.Callback>()
|
||||
|
||||
override val userId: Int = userId
|
||||
override val userHandle: UserHandle = userHandle
|
||||
override val userInfo: UserInfo = userInfo
|
||||
override val userProfiles: List<UserInfo> = userProfiles
|
||||
override val userId: Int
|
||||
get() = _userId
|
||||
override val userHandle: UserHandle
|
||||
get() = _userHandle
|
||||
override val userInfo: UserInfo
|
||||
get() = _userInfo
|
||||
override val userProfiles: List<UserInfo>
|
||||
get() = _userProfiles
|
||||
|
||||
override val userContentResolver: ContentResolver = userContentResolver
|
||||
override val userContext: Context = userContext
|
||||
@@ -55,4 +59,13 @@ class FakeUserTracker(
|
||||
override fun createCurrentUserContext(context: Context): Context {
|
||||
return onCreateCurrentUserContext(context)
|
||||
}
|
||||
|
||||
fun set(userInfos: List<UserInfo>, selectedUserIndex: Int) {
|
||||
_userProfiles = userInfos
|
||||
_userInfo = userInfos[selectedUserIndex]
|
||||
_userId = _userInfo.id
|
||||
_userHandle = UserHandle.of(_userId)
|
||||
|
||||
callbacks.forEach { it.onUserChanged(_userId, userContext) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,18 @@
|
||||
|
||||
package com.android.systemui.user.data.repository
|
||||
|
||||
import android.content.pm.UserInfo
|
||||
import android.os.UserHandle
|
||||
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
||||
import com.android.systemui.user.shared.model.UserActionModel
|
||||
import com.android.systemui.user.shared.model.UserModel
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.yield
|
||||
|
||||
class FakeUserRepository : UserRepository {
|
||||
|
||||
@@ -34,21 +40,71 @@ class FakeUserRepository : UserRepository {
|
||||
private val _actions = MutableStateFlow<List<UserActionModel>>(emptyList())
|
||||
override val actions: Flow<List<UserActionModel>> = _actions.asStateFlow()
|
||||
|
||||
private val _userSwitcherSettings = MutableStateFlow(UserSwitcherSettingsModel())
|
||||
override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> =
|
||||
_userSwitcherSettings.asStateFlow()
|
||||
|
||||
private val _userInfos = MutableStateFlow<List<UserInfo>>(emptyList())
|
||||
override val userInfos: Flow<List<UserInfo>> = _userInfos.asStateFlow()
|
||||
|
||||
private val _selectedUserInfo = MutableStateFlow<UserInfo?>(null)
|
||||
override val selectedUserInfo: Flow<UserInfo> = _selectedUserInfo.filterNotNull()
|
||||
|
||||
override var lastSelectedNonGuestUserId: Int = UserHandle.USER_SYSTEM
|
||||
|
||||
private val _isActionableWhenLocked = MutableStateFlow(false)
|
||||
override val isActionableWhenLocked: Flow<Boolean> = _isActionableWhenLocked.asStateFlow()
|
||||
|
||||
private var _isGuestUserAutoCreated: Boolean = false
|
||||
override val isGuestUserAutoCreated: Boolean
|
||||
get() = _isGuestUserAutoCreated
|
||||
private var _isGuestUserResetting: Boolean = false
|
||||
override val isGuestUserResetting: Boolean
|
||||
get() = _isGuestUserResetting
|
||||
|
||||
override var isGuestUserResetting: Boolean = false
|
||||
|
||||
override val isGuestUserCreationScheduled = AtomicBoolean()
|
||||
|
||||
override var secondaryUserId: Int = UserHandle.USER_NULL
|
||||
|
||||
override var isRefreshUsersPaused: Boolean = false
|
||||
|
||||
var refreshUsersCallCount: Int = 0
|
||||
private set
|
||||
|
||||
override fun refreshUsers() {
|
||||
refreshUsersCallCount++
|
||||
}
|
||||
|
||||
override fun getSelectedUserInfo(): UserInfo {
|
||||
return checkNotNull(_selectedUserInfo.value)
|
||||
}
|
||||
|
||||
override fun isSimpleUserSwitcher(): Boolean {
|
||||
return _userSwitcherSettings.value.isSimpleUserSwitcher
|
||||
}
|
||||
|
||||
fun setUserInfos(infos: List<UserInfo>) {
|
||||
_userInfos.value = infos
|
||||
}
|
||||
|
||||
suspend fun setSelectedUserInfo(userInfo: UserInfo) {
|
||||
check(_userInfos.value.contains(userInfo)) {
|
||||
"Cannot select the following user, it is not in the list of user infos: $userInfo!"
|
||||
}
|
||||
|
||||
_selectedUserInfo.value = userInfo
|
||||
yield()
|
||||
}
|
||||
|
||||
suspend fun setSettings(settings: UserSwitcherSettingsModel) {
|
||||
_userSwitcherSettings.value = settings
|
||||
yield()
|
||||
}
|
||||
|
||||
fun setUsers(models: List<UserModel>) {
|
||||
_users.value = models
|
||||
}
|
||||
|
||||
fun setSelectedUser(userId: Int) {
|
||||
suspend fun setSelectedUser(userId: Int) {
|
||||
check(_users.value.find { it.id == userId } != null) {
|
||||
"Cannot select a user with ID $userId - no user with that ID found!"
|
||||
}
|
||||
@@ -62,6 +118,7 @@ class FakeUserRepository : UserRepository {
|
||||
}
|
||||
}
|
||||
)
|
||||
yield()
|
||||
}
|
||||
|
||||
fun setActions(models: List<UserActionModel>) {
|
||||
@@ -75,8 +132,4 @@ class FakeUserRepository : UserRepository {
|
||||
fun setGuestUserAutoCreated(value: Boolean) {
|
||||
_isGuestUserAutoCreated = value
|
||||
}
|
||||
|
||||
fun setGuestUserResetting(value: Boolean) {
|
||||
_isGuestUserResetting = value
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user