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:
Alejandro Nijamkin
2022-08-31 15:54:11 -07:00
parent 2aca8b4dad
commit 2ee73627d2
2 changed files with 323 additions and 0 deletions

View File

@@ -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,
)
}
}
}

View File

@@ -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
}
}