User domain layer.
UserSwitcherActivity refactor: CL 5/7 This CL adds the domain layer, which is just the UserInteractor really. Bug: 243844359 Test: Unit tests included. Also verified manually as part of the testing of CL 7/7 in the chain. Change-Id: I83e45a0060ab746874107c79bc8a1e2aeb07e409
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.domain.interactor
|
||||
|
||||
import android.content.Intent
|
||||
import android.provider.Settings
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
||||
import com.android.systemui.user.data.repository.UserRepository
|
||||
import com.android.systemui.user.shared.model.UserActionModel
|
||||
import com.android.systemui.user.shared.model.UserModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/** Encapsulates business logic to interact with user data and systems. */
|
||||
@SysUISingleton
|
||||
class UserInteractor
|
||||
@Inject
|
||||
constructor(
|
||||
repository: UserRepository,
|
||||
private val controller: UserSwitcherController,
|
||||
private val activityStarter: ActivityStarter,
|
||||
keyguardInteractor: KeyguardInteractor,
|
||||
) {
|
||||
/** List of current on-device users to select from. */
|
||||
val users: Flow<List<UserModel>> = repository.users
|
||||
|
||||
/** The currently-selected user. */
|
||||
val selectedUser: Flow<UserModel> = repository.selectedUser
|
||||
|
||||
/** List of user-switcher related actions that are available. */
|
||||
val actions: Flow<List<UserActionModel>> =
|
||||
combine(
|
||||
repository.isActionableWhenLocked,
|
||||
keyguardInteractor.isKeyguardShowing,
|
||||
) { isActionableWhenLocked, isLocked ->
|
||||
isActionableWhenLocked || !isLocked
|
||||
}
|
||||
.flatMapLatest { isActionable ->
|
||||
if (isActionable) {
|
||||
repository.actions.map { actions ->
|
||||
actions +
|
||||
if (actions.isNotEmpty()) {
|
||||
// If we have actions, we add NAVIGATE_TO_USER_MANAGEMENT because
|
||||
// that's a user
|
||||
// switcher specific action that is not known to the our data source
|
||||
// or other
|
||||
// features.
|
||||
listOf(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
|
||||
} else {
|
||||
// If no actions, don't add the navigate action.
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If not actionable it means that we're not allowed to show actions when locked
|
||||
// and we
|
||||
// are locked. Therefore, we should show no actions.
|
||||
flowOf(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether the device is configured to always have a guest user available. */
|
||||
val isGuestUserAutoCreated: Boolean = repository.isGuestUserAutoCreated
|
||||
|
||||
/** Whether the guest user is currently being reset. */
|
||||
val isGuestUserResetting: Boolean = repository.isGuestUserResetting
|
||||
|
||||
/** Switches to the user with the given user ID. */
|
||||
fun selectUser(
|
||||
userId: Int,
|
||||
) {
|
||||
controller.onUserSelected(userId, /* dialogShower= */ null)
|
||||
}
|
||||
|
||||
/** Executes the given action. */
|
||||
fun executeAction(action: UserActionModel) {
|
||||
when (action) {
|
||||
UserActionModel.ENTER_GUEST_MODE -> controller.createAndSwitchToGuestUser(null)
|
||||
UserActionModel.ADD_USER -> controller.showAddUserDialog(null)
|
||||
UserActionModel.ADD_SUPERVISED_USER -> controller.startSupervisedUserActivity()
|
||||
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT ->
|
||||
activityStarter.startActivity(
|
||||
Intent(Settings.ACTION_USER_SETTINGS),
|
||||
/* dismissShade= */ false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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.domain.interactor
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
||||
import com.android.systemui.user.data.repository.FakeUserRepository
|
||||
import com.android.systemui.user.shared.model.UserActionModel
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.android.systemui.util.mockito.nullable
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
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.Mock
|
||||
import org.mockito.Mockito.anyBoolean
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class UserInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Mock private lateinit var controller: UserSwitcherController
|
||||
@Mock private lateinit var activityStarter: ActivityStarter
|
||||
|
||||
private lateinit var underTest: UserInteractor
|
||||
|
||||
private lateinit var userRepository: FakeUserRepository
|
||||
private lateinit var keyguardRepository: FakeKeyguardRepository
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
userRepository = FakeUserRepository()
|
||||
keyguardRepository = FakeKeyguardRepository()
|
||||
underTest =
|
||||
UserInteractor(
|
||||
repository = userRepository,
|
||||
controller = controller,
|
||||
activityStarter = activityStarter,
|
||||
keyguardInteractor =
|
||||
KeyguardInteractor(
|
||||
repository = keyguardRepository,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actions - not actionable when locked and locked - no actions`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
userRepository.setActions(UserActionModel.values().toList())
|
||||
userRepository.setActionableWhenLocked(false)
|
||||
keyguardRepository.setKeyguardShowing(true)
|
||||
|
||||
var actions: List<UserActionModel>? = null
|
||||
val job = underTest.actions.onEach { actions = it }.launchIn(this)
|
||||
|
||||
assertThat(actions).isEmpty()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actions - not actionable when locked and not locked`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
userRepository.setActions(
|
||||
listOf(
|
||||
UserActionModel.ENTER_GUEST_MODE,
|
||||
UserActionModel.ADD_USER,
|
||||
UserActionModel.ADD_SUPERVISED_USER,
|
||||
)
|
||||
)
|
||||
userRepository.setActionableWhenLocked(false)
|
||||
keyguardRepository.setKeyguardShowing(false)
|
||||
|
||||
var actions: List<UserActionModel>? = null
|
||||
val job = underTest.actions.onEach { actions = it }.launchIn(this)
|
||||
|
||||
assertThat(actions)
|
||||
.isEqualTo(
|
||||
listOf(
|
||||
UserActionModel.ENTER_GUEST_MODE,
|
||||
UserActionModel.ADD_USER,
|
||||
UserActionModel.ADD_SUPERVISED_USER,
|
||||
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
||||
)
|
||||
)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actions - actionable when locked and not locked`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
userRepository.setActions(
|
||||
listOf(
|
||||
UserActionModel.ENTER_GUEST_MODE,
|
||||
UserActionModel.ADD_USER,
|
||||
UserActionModel.ADD_SUPERVISED_USER,
|
||||
)
|
||||
)
|
||||
userRepository.setActionableWhenLocked(true)
|
||||
keyguardRepository.setKeyguardShowing(false)
|
||||
|
||||
var actions: List<UserActionModel>? = null
|
||||
val job = underTest.actions.onEach { actions = it }.launchIn(this)
|
||||
|
||||
assertThat(actions)
|
||||
.isEqualTo(
|
||||
listOf(
|
||||
UserActionModel.ENTER_GUEST_MODE,
|
||||
UserActionModel.ADD_USER,
|
||||
UserActionModel.ADD_SUPERVISED_USER,
|
||||
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
||||
)
|
||||
)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `actions - actionable when locked and locked`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
userRepository.setActions(
|
||||
listOf(
|
||||
UserActionModel.ENTER_GUEST_MODE,
|
||||
UserActionModel.ADD_USER,
|
||||
UserActionModel.ADD_SUPERVISED_USER,
|
||||
)
|
||||
)
|
||||
userRepository.setActionableWhenLocked(true)
|
||||
keyguardRepository.setKeyguardShowing(true)
|
||||
|
||||
var actions: List<UserActionModel>? = null
|
||||
val job = underTest.actions.onEach { actions = it }.launchIn(this)
|
||||
|
||||
assertThat(actions)
|
||||
.isEqualTo(
|
||||
listOf(
|
||||
UserActionModel.ENTER_GUEST_MODE,
|
||||
UserActionModel.ADD_USER,
|
||||
UserActionModel.ADD_SUPERVISED_USER,
|
||||
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
||||
)
|
||||
)
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun selectUser() {
|
||||
val userId = 3
|
||||
|
||||
underTest.selectUser(userId)
|
||||
|
||||
verify(controller).onUserSelected(eq(userId), nullable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `executeAction - guest`() {
|
||||
underTest.executeAction(UserActionModel.ENTER_GUEST_MODE)
|
||||
|
||||
verify(controller).createAndSwitchToGuestUser(nullable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `executeAction - add user`() {
|
||||
underTest.executeAction(UserActionModel.ADD_USER)
|
||||
|
||||
verify(controller).showAddUserDialog(nullable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `executeAction - add supervised user`() {
|
||||
underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)
|
||||
|
||||
verify(controller).startSupervisedUserActivity()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `executeAction - manage users`() {
|
||||
underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
|
||||
|
||||
verify(activityStarter).startActivity(any(), anyBoolean())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user