Merge "Cleans up old user switcher impl. and flags." into tm-qpr-dev
This commit is contained in:
@@ -1,392 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.ui.compose
|
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.border
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.layout.sizeIn
|
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.material3.DropdownMenu
|
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.alpha
|
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
|
||||||
import androidx.compose.ui.graphics.painter.ColorPainter
|
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
|
||||||
import androidx.compose.ui.res.colorResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
|
||||||
import com.android.systemui.common.ui.compose.load
|
|
||||||
import com.android.systemui.compose.SysUiOutlinedButton
|
|
||||||
import com.android.systemui.compose.SysUiTextButton
|
|
||||||
import com.android.systemui.compose.features.R
|
|
||||||
import com.android.systemui.compose.theme.LocalAndroidColorScheme
|
|
||||||
import com.android.systemui.user.ui.viewmodel.UserActionViewModel
|
|
||||||
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
|
|
||||||
import com.android.systemui.user.ui.viewmodel.UserViewModel
|
|
||||||
import java.lang.Integer.min
|
|
||||||
import kotlin.math.ceil
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun UserSwitcherScreen(
|
|
||||||
viewModel: UserSwitcherViewModel,
|
|
||||||
onFinished: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val isFinishRequested: Boolean by viewModel.isFinishRequested.collectAsState(false)
|
|
||||||
val users: List<UserViewModel> by viewModel.users.collectAsState(emptyList())
|
|
||||||
val maxUserColumns: Int by viewModel.maximumUserColumns.collectAsState(1)
|
|
||||||
val menuActions: List<UserActionViewModel> by viewModel.menu.collectAsState(emptyList())
|
|
||||||
val isOpenMenuButtonVisible: Boolean by viewModel.isOpenMenuButtonVisible.collectAsState(false)
|
|
||||||
val isMenuVisible: Boolean by viewModel.isMenuVisible.collectAsState(false)
|
|
||||||
|
|
||||||
UserSwitcherScreenStateless(
|
|
||||||
isFinishRequested = isFinishRequested,
|
|
||||||
users = users,
|
|
||||||
maxUserColumns = maxUserColumns,
|
|
||||||
menuActions = menuActions,
|
|
||||||
isOpenMenuButtonVisible = isOpenMenuButtonVisible,
|
|
||||||
isMenuVisible = isMenuVisible,
|
|
||||||
onMenuClosed = viewModel::onMenuClosed,
|
|
||||||
onOpenMenuButtonClicked = viewModel::onOpenMenuButtonClicked,
|
|
||||||
onCancelButtonClicked = viewModel::onCancelButtonClicked,
|
|
||||||
onFinished = {
|
|
||||||
onFinished()
|
|
||||||
viewModel.onFinished()
|
|
||||||
},
|
|
||||||
modifier = modifier,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun UserSwitcherScreenStateless(
|
|
||||||
isFinishRequested: Boolean,
|
|
||||||
users: List<UserViewModel>,
|
|
||||||
maxUserColumns: Int,
|
|
||||||
menuActions: List<UserActionViewModel>,
|
|
||||||
isOpenMenuButtonVisible: Boolean,
|
|
||||||
isMenuVisible: Boolean,
|
|
||||||
onMenuClosed: () -> Unit,
|
|
||||||
onOpenMenuButtonClicked: () -> Unit,
|
|
||||||
onCancelButtonClicked: () -> Unit,
|
|
||||||
onFinished: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
LaunchedEffect(isFinishRequested) {
|
|
||||||
if (isFinishRequested) {
|
|
||||||
onFinished()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier =
|
|
||||||
modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(
|
|
||||||
horizontal = 60.dp,
|
|
||||||
vertical = 40.dp,
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
UserGrid(
|
|
||||||
users = users,
|
|
||||||
maxUserColumns = maxUserColumns,
|
|
||||||
modifier = Modifier.align(Alignment.Center),
|
|
||||||
)
|
|
||||||
|
|
||||||
Buttons(
|
|
||||||
menuActions = menuActions,
|
|
||||||
isOpenMenuButtonVisible = isOpenMenuButtonVisible,
|
|
||||||
isMenuVisible = isMenuVisible,
|
|
||||||
onMenuClosed = onMenuClosed,
|
|
||||||
onOpenMenuButtonClicked = onOpenMenuButtonClicked,
|
|
||||||
onCancelButtonClicked = onCancelButtonClicked,
|
|
||||||
modifier = Modifier.align(Alignment.BottomEnd),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun UserGrid(
|
|
||||||
users: List<UserViewModel>,
|
|
||||||
maxUserColumns: Int,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.spacedBy(44.dp),
|
|
||||||
modifier = modifier,
|
|
||||||
) {
|
|
||||||
val rowCount = ceil(users.size / maxUserColumns.toFloat()).toInt()
|
|
||||||
(0 until rowCount).forEach { rowIndex ->
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(64.dp),
|
|
||||||
modifier = modifier,
|
|
||||||
) {
|
|
||||||
val fromIndex = rowIndex * maxUserColumns
|
|
||||||
val toIndex = min(users.size, (rowIndex + 1) * maxUserColumns)
|
|
||||||
users.subList(fromIndex, toIndex).forEach { user ->
|
|
||||||
UserItem(
|
|
||||||
viewModel = user,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun UserItem(
|
|
||||||
viewModel: UserViewModel,
|
|
||||||
) {
|
|
||||||
val onClicked = viewModel.onClicked
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
modifier =
|
|
||||||
if (onClicked != null) {
|
|
||||||
Modifier.clickable { onClicked() }
|
|
||||||
} else {
|
|
||||||
Modifier
|
|
||||||
}
|
|
||||||
.alpha(viewModel.alpha),
|
|
||||||
) {
|
|
||||||
Box {
|
|
||||||
UserItemBackground(modifier = Modifier.align(Alignment.Center).size(222.dp))
|
|
||||||
|
|
||||||
UserItemIcon(
|
|
||||||
image = viewModel.image,
|
|
||||||
isSelectionMarkerVisible = viewModel.isSelectionMarkerVisible,
|
|
||||||
modifier = Modifier.align(Alignment.Center).size(222.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// User name
|
|
||||||
val text = viewModel.name.load()
|
|
||||||
if (text != null) {
|
|
||||||
// We use the box to center-align the text vertically as that is not possible with Text
|
|
||||||
// alone.
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.size(width = 222.dp, height = 48.dp),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = text,
|
|
||||||
style = MaterialTheme.typography.titleLarge,
|
|
||||||
color = colorResource(com.android.internal.R.color.system_neutral1_50),
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier = Modifier.align(Alignment.Center),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun UserItemBackground(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Image(
|
|
||||||
painter = ColorPainter(LocalAndroidColorScheme.current.colorBackground),
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = modifier.clip(CircleShape),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun UserItemIcon(
|
|
||||||
image: Drawable,
|
|
||||||
isSelectionMarkerVisible: Boolean,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Image(
|
|
||||||
bitmap = image.toBitmap().asImageBitmap(),
|
|
||||||
contentDescription = null,
|
|
||||||
modifier =
|
|
||||||
if (isSelectionMarkerVisible) {
|
|
||||||
// Draws a ring
|
|
||||||
modifier.border(
|
|
||||||
width = 8.dp,
|
|
||||||
color = LocalAndroidColorScheme.current.colorAccentPrimary,
|
|
||||||
shape = CircleShape,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
modifier
|
|
||||||
}
|
|
||||||
.padding(16.dp)
|
|
||||||
.clip(CircleShape)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun Buttons(
|
|
||||||
menuActions: List<UserActionViewModel>,
|
|
||||||
isOpenMenuButtonVisible: Boolean,
|
|
||||||
isMenuVisible: Boolean,
|
|
||||||
onMenuClosed: () -> Unit,
|
|
||||||
onOpenMenuButtonClicked: () -> Unit,
|
|
||||||
onCancelButtonClicked: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = modifier,
|
|
||||||
) {
|
|
||||||
// Cancel button.
|
|
||||||
SysUiTextButton(
|
|
||||||
onClick = onCancelButtonClicked,
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.cancel))
|
|
||||||
}
|
|
||||||
|
|
||||||
// "Open menu" button.
|
|
||||||
if (isOpenMenuButtonVisible) {
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
// To properly use a DropdownMenu in Compose, we need to wrap the button that opens it
|
|
||||||
// and the menu itself in a Box.
|
|
||||||
Box {
|
|
||||||
SysUiOutlinedButton(
|
|
||||||
onClick = onOpenMenuButtonClicked,
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.add))
|
|
||||||
}
|
|
||||||
Menu(
|
|
||||||
viewModel = menuActions,
|
|
||||||
isMenuVisible = isMenuVisible,
|
|
||||||
onMenuClosed = onMenuClosed,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun Menu(
|
|
||||||
viewModel: List<UserActionViewModel>,
|
|
||||||
isMenuVisible: Boolean,
|
|
||||||
onMenuClosed: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val maxItemWidth = LocalConfiguration.current.screenWidthDp.dp / 4
|
|
||||||
DropdownMenu(
|
|
||||||
expanded = isMenuVisible,
|
|
||||||
onDismissRequest = onMenuClosed,
|
|
||||||
modifier =
|
|
||||||
modifier.background(
|
|
||||||
color = MaterialTheme.colorScheme.inverseOnSurface,
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
viewModel.forEachIndexed { index, action ->
|
|
||||||
MenuItem(
|
|
||||||
viewModel = action,
|
|
||||||
onClicked = { action.onClicked() },
|
|
||||||
topPadding =
|
|
||||||
if (index == 0) {
|
|
||||||
16.dp
|
|
||||||
} else {
|
|
||||||
0.dp
|
|
||||||
},
|
|
||||||
bottomPadding =
|
|
||||||
if (index == viewModel.size - 1) {
|
|
||||||
16.dp
|
|
||||||
} else {
|
|
||||||
0.dp
|
|
||||||
},
|
|
||||||
modifier = Modifier.sizeIn(maxWidth = maxItemWidth),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MenuItem(
|
|
||||||
viewModel: UserActionViewModel,
|
|
||||||
onClicked: () -> Unit,
|
|
||||||
topPadding: Dp,
|
|
||||||
bottomPadding: Dp,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val density = LocalDensity.current
|
|
||||||
|
|
||||||
val icon =
|
|
||||||
remember(viewModel.iconResourceId) {
|
|
||||||
val drawable =
|
|
||||||
checkNotNull(AppCompatResources.getDrawable(context, viewModel.iconResourceId))
|
|
||||||
val size = with(density) { 20.dp.toPx() }.toInt()
|
|
||||||
drawable
|
|
||||||
.toBitmap(
|
|
||||||
width = size,
|
|
||||||
height = size,
|
|
||||||
)
|
|
||||||
.asImageBitmap()
|
|
||||||
}
|
|
||||||
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = {
|
|
||||||
Text(
|
|
||||||
text = stringResource(viewModel.textResourceId),
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onClick = onClicked,
|
|
||||||
leadingIcon = {
|
|
||||||
Spacer(modifier = Modifier.width(10.dp))
|
|
||||||
Image(
|
|
||||||
bitmap = icon,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
modifier =
|
|
||||||
modifier
|
|
||||||
.heightIn(
|
|
||||||
min = 56.dp,
|
|
||||||
)
|
|
||||||
.padding(
|
|
||||||
start = 18.dp,
|
|
||||||
end = 65.dp,
|
|
||||||
top = topPadding,
|
|
||||||
bottom = bottomPadding,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -114,28 +114,6 @@ object Flags {
|
|||||||
// TODO(b/254512385): Tracking Bug
|
// TODO(b/254512385): Tracking Bug
|
||||||
@JvmField val MODERN_BOUNCER = releasedFlag(208, "modern_bouncer")
|
@JvmField val MODERN_BOUNCER = releasedFlag(208, "modern_bouncer")
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the user interactor and repository should use `UserSwitcherController`.
|
|
||||||
*
|
|
||||||
* If this is `false`, the interactor and repo skip the controller and directly access the
|
|
||||||
* framework APIs.
|
|
||||||
*/
|
|
||||||
// TODO(b/254513286): Tracking Bug
|
|
||||||
val USER_INTERACTOR_AND_REPO_USE_CONTROLLER =
|
|
||||||
unreleasedFlag(210, "user_interactor_and_repo_use_controller")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether `UserSwitcherController` should use the user interactor.
|
|
||||||
*
|
|
||||||
* When this is `true`, the controller does not directly access framework APIs. Instead, it goes
|
|
||||||
* through the interactor.
|
|
||||||
*
|
|
||||||
* Note: do not set this to true if [.USER_INTERACTOR_AND_REPO_USE_CONTROLLER] is `true` as it
|
|
||||||
* would created a cycle between controller -> interactor -> controller.
|
|
||||||
*/
|
|
||||||
// TODO(b/254513102): Tracking Bug
|
|
||||||
val USER_CONTROLLER_USES_INTERACTOR = releasedFlag(211, "user_controller_uses_interactor")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the clock on a wide lock screen should use the new "stepping" animation for moving
|
* Whether the clock on a wide lock screen should use the new "stepping" animation for moving
|
||||||
* the digits when the clock moves.
|
* the digits when the clock moves.
|
||||||
|
|||||||
@@ -1148,7 +1148,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
|
|||||||
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
|
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
|
||||||
mNotificationIconAreaController.setupShelf(mNotificationShelfController);
|
mNotificationIconAreaController.setupShelf(mNotificationShelfController);
|
||||||
mShadeExpansionStateManager.addExpansionListener(mWakeUpCoordinator);
|
mShadeExpansionStateManager.addExpansionListener(mWakeUpCoordinator);
|
||||||
mUserSwitcherController.init(mNotificationShadeWindowView);
|
|
||||||
|
|
||||||
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
|
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
|
||||||
mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
|
mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
|
||||||
@@ -4285,7 +4284,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
|
|||||||
}
|
}
|
||||||
// TODO: Bring these out of CentralSurfaces.
|
// TODO: Bring these out of CentralSurfaces.
|
||||||
mUserInfoControllerImpl.onDensityOrFontScaleChanged();
|
mUserInfoControllerImpl.onDensityOrFontScaleChanged();
|
||||||
mUserSwitcherController.onDensityOrFontScaleChanged();
|
|
||||||
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
|
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
|
||||||
mHeadsUpManager.onDensityOrFontScaleChanged();
|
mHeadsUpManager.onDensityOrFontScaleChanged();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import android.graphics.ColorFilter
|
|||||||
import android.graphics.ColorMatrix
|
import android.graphics.ColorMatrix
|
||||||
import android.graphics.ColorMatrixColorFilter
|
import android.graphics.ColorMatrixColorFilter
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.UserHandle
|
|
||||||
import android.widget.BaseAdapter
|
import android.widget.BaseAdapter
|
||||||
import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
|
import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
|
||||||
import com.android.systemui.user.data.source.UserRecord
|
import com.android.systemui.user.data.source.UserRecord
|
||||||
@@ -84,7 +83,7 @@ protected constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
controller.refreshUsers(UserHandle.USER_NULL)
|
controller.refreshUsers()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -14,35 +14,74 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.systemui.statusbar.policy
|
package com.android.systemui.statusbar.policy
|
||||||
|
|
||||||
import android.annotation.UserIdInt
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.android.systemui.Dumpable
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
|
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||||
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
|
import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
|
||||||
import com.android.systemui.user.data.source.UserRecord
|
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.ui.LegacyUserUiHelper
|
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
|
||||||
|
import dagger.Lazy
|
||||||
|
import java.io.PrintWriter
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import kotlinx.coroutines.flow.Flow
|
import javax.inject.Inject
|
||||||
|
|
||||||
/** Defines interface for a class that provides user switching functionality and state. */
|
/** Access point into multi-user switching logic. */
|
||||||
interface UserSwitcherController : Dumpable {
|
@Deprecated("Use UserInteractor or GuestUserInteractor instead.")
|
||||||
|
@SysUISingleton
|
||||||
|
class UserSwitcherController
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
@Application private val applicationContext: Context,
|
||||||
|
private val userInteractorLazy: Lazy<UserInteractor>,
|
||||||
|
private val guestUserInteractorLazy: Lazy<GuestUserInteractor>,
|
||||||
|
private val keyguardInteractorLazy: Lazy<KeyguardInteractor>,
|
||||||
|
private val activityStarter: ActivityStarter,
|
||||||
|
) {
|
||||||
|
|
||||||
|
/** Defines interface for classes that can be called back when the user is switched. */
|
||||||
|
fun interface UserSwitchCallback {
|
||||||
|
/** Notifies that the user has switched. */
|
||||||
|
fun onUserSwitched()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 val callbackCompatMap = mutableMapOf<UserSwitchCallback, UserInteractor.UserCallback>()
|
||||||
|
|
||||||
/** The current list of [UserRecord]. */
|
/** The current list of [UserRecord]. */
|
||||||
val users: ArrayList<UserRecord>
|
val users: ArrayList<UserRecord>
|
||||||
|
get() = userInteractor.userRecords.value
|
||||||
|
|
||||||
/** Whether the user switcher experience should use the simple experience. */
|
/** Whether the user switcher experience should use the simple experience. */
|
||||||
val isSimpleUserSwitcher: Boolean
|
val isSimpleUserSwitcher: Boolean
|
||||||
|
get() = userInteractor.isSimpleUserSwitcher
|
||||||
/** Require a view for jank detection */
|
|
||||||
fun init(view: View)
|
|
||||||
|
|
||||||
/** The [UserRecord] of the current user or `null` when none. */
|
/** The [UserRecord] of the current user or `null` when none. */
|
||||||
val currentUserRecord: UserRecord?
|
val currentUserRecord: UserRecord?
|
||||||
|
get() = userInteractor.selectedUserRecord.value
|
||||||
|
|
||||||
/** The name of the current user of the device or `null`, when none is selected. */
|
/** The name of the current user of the device or `null`, when none is selected. */
|
||||||
val currentUserName: String?
|
val currentUserName: String?
|
||||||
|
get() =
|
||||||
|
currentUserRecord?.let {
|
||||||
|
LegacyUserUiHelper.getUserRecordName(
|
||||||
|
context = applicationContext,
|
||||||
|
record = it,
|
||||||
|
isGuestUserAutoCreated = userInteractor.isGuestUserAutoCreated,
|
||||||
|
isGuestUserResetting = userInteractor.isGuestUserResetting,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies that a user has been selected.
|
* Notifies that a user has been selected.
|
||||||
@@ -55,34 +94,40 @@ interface UserSwitcherController : Dumpable {
|
|||||||
* @param userId The ID of the user to switch to.
|
* @param userId The ID of the user to switch to.
|
||||||
* @param dialogShower An optional [DialogShower] in case we need to show dialogs.
|
* @param dialogShower An optional [DialogShower] in case we need to show dialogs.
|
||||||
*/
|
*/
|
||||||
fun onUserSelected(userId: Int, dialogShower: DialogShower?)
|
fun onUserSelected(userId: Int, dialogShower: DialogShower?) {
|
||||||
|
userInteractor.selectUser(userId, dialogShower)
|
||||||
/** Whether it is allowed to add users while the device is locked. */
|
}
|
||||||
val isAddUsersFromLockScreenEnabled: Flow<Boolean>
|
|
||||||
|
|
||||||
/** Whether the guest user is configured to always be present on the device. */
|
/** Whether the guest user is configured to always be present on the device. */
|
||||||
val isGuestUserAutoCreated: Boolean
|
val isGuestUserAutoCreated: Boolean
|
||||||
|
get() = userInteractor.isGuestUserAutoCreated
|
||||||
|
|
||||||
/** Whether the guest user is currently being reset. */
|
/** Whether the guest user is currently being reset. */
|
||||||
val isGuestUserResetting: Boolean
|
val isGuestUserResetting: Boolean
|
||||||
|
get() = userInteractor.isGuestUserResetting
|
||||||
/** Creates and switches to the guest user. */
|
|
||||||
fun createAndSwitchToGuestUser(dialogShower: DialogShower?)
|
|
||||||
|
|
||||||
/** Shows the add user dialog. */
|
|
||||||
fun showAddUserDialog(dialogShower: DialogShower?)
|
|
||||||
|
|
||||||
/** Starts an activity to add a supervised user to the device. */
|
|
||||||
fun startSupervisedUserActivity()
|
|
||||||
|
|
||||||
/** Notifies when the display density or font scale has changed. */
|
|
||||||
fun onDensityOrFontScaleChanged()
|
|
||||||
|
|
||||||
/** Registers an adapter to notify when the users change. */
|
/** Registers an adapter to notify when the users change. */
|
||||||
fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>)
|
fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) {
|
||||||
|
userInteractor.addCallback(
|
||||||
|
object : UserInteractor.UserCallback {
|
||||||
|
override fun isEvictable(): Boolean {
|
||||||
|
return adapter.get() == null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUserStateChanged() {
|
||||||
|
adapter.get()?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/** Notifies the item for a user has been clicked. */
|
/** Notifies the item for a user has been clicked. */
|
||||||
fun onUserListItemClicked(record: UserRecord, dialogShower: DialogShower?)
|
fun onUserListItemClicked(
|
||||||
|
record: UserRecord,
|
||||||
|
dialogShower: DialogShower?,
|
||||||
|
) {
|
||||||
|
userInteractor.onRecordSelected(record, dialogShower)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes guest user and switches to target user. The guest must be the current user and its id
|
* Removes guest user and switches to target user. The guest must be the current user and its id
|
||||||
@@ -103,7 +148,12 @@ interface UserSwitcherController : Dumpable {
|
|||||||
* @param targetUserId id of the user to switch to after guest is removed. If
|
* @param targetUserId id of the user to switch to after guest is removed. If
|
||||||
* `UserHandle.USER_NULL`, then switch immediately to the newly created guest user.
|
* `UserHandle.USER_NULL`, then switch immediately to the newly created guest user.
|
||||||
*/
|
*/
|
||||||
fun removeGuestUser(@UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int)
|
fun removeGuestUser(guestUserId: Int, targetUserId: Int) {
|
||||||
|
userInteractor.removeGuestUser(
|
||||||
|
guestUserId = guestUserId,
|
||||||
|
targetUserId = targetUserId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exits guest user and switches to previous non-guest user. The guest must be the current user.
|
* Exits guest user and switches to previous non-guest user. The guest must be the current user.
|
||||||
@@ -114,43 +164,58 @@ interface UserSwitcherController : Dumpable {
|
|||||||
* @param forceRemoveGuestOnExit true: remove guest before switching user, false: remove guest
|
* @param forceRemoveGuestOnExit true: remove guest before switching user, false: remove guest
|
||||||
* only if its ephemeral, else keep guest
|
* only if its ephemeral, else keep guest
|
||||||
*/
|
*/
|
||||||
fun exitGuestUser(
|
fun exitGuestUser(guestUserId: Int, targetUserId: Int, forceRemoveGuestOnExit: Boolean) {
|
||||||
@UserIdInt guestUserId: Int,
|
userInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit)
|
||||||
@UserIdInt targetUserId: Int,
|
}
|
||||||
forceRemoveGuestOnExit: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guarantee guest is present only if the device is provisioned. Otherwise, create a content
|
* Guarantee guest is present only if the device is provisioned. Otherwise, create a content
|
||||||
* observer to wait until the device is provisioned, then schedule the guest creation.
|
* observer to wait until the device is provisioned, then schedule the guest creation.
|
||||||
*/
|
*/
|
||||||
fun schedulePostBootGuestCreation()
|
fun schedulePostBootGuestCreation() {
|
||||||
|
guestUserInteractor.onDeviceBootCompleted()
|
||||||
|
}
|
||||||
|
|
||||||
/** Whether keyguard is showing. */
|
/** Whether keyguard is showing. */
|
||||||
val isKeyguardShowing: Boolean
|
val isKeyguardShowing: Boolean
|
||||||
|
get() = keyguardInteractor.isKeyguardShowing()
|
||||||
|
|
||||||
/** Starts an activity with the given [Intent]. */
|
/** Starts an activity with the given [Intent]. */
|
||||||
fun startActivity(intent: Intent)
|
fun startActivity(intent: Intent) {
|
||||||
|
activityStarter.startActivity(intent, /* dismissShade= */ true)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes users from UserManager.
|
* Refreshes users from UserManager.
|
||||||
*
|
*
|
||||||
* The pictures are only loaded if they have not been loaded yet.
|
* The pictures are only loaded if they have not been loaded yet.
|
||||||
*
|
|
||||||
* @param forcePictureLoadForId forces the picture of the given user to be reloaded.
|
|
||||||
*/
|
*/
|
||||||
fun refreshUsers(forcePictureLoadForId: Int)
|
fun refreshUsers() {
|
||||||
|
userInteractor.refreshUsers()
|
||||||
|
}
|
||||||
|
|
||||||
/** Adds a subscriber to when user switches. */
|
/** Adds a subscriber to when user switches. */
|
||||||
fun addUserSwitchCallback(callback: UserSwitchCallback)
|
fun addUserSwitchCallback(callback: UserSwitchCallback) {
|
||||||
|
val interactorCallback =
|
||||||
|
object : UserInteractor.UserCallback {
|
||||||
|
override fun onUserStateChanged() {
|
||||||
|
callback.onUserSwitched()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callbackCompatMap[callback] = interactorCallback
|
||||||
|
userInteractor.addCallback(interactorCallback)
|
||||||
|
}
|
||||||
|
|
||||||
/** Removes a previously-added subscriber. */
|
/** Removes a previously-added subscriber. */
|
||||||
fun removeUserSwitchCallback(callback: UserSwitchCallback)
|
fun removeUserSwitchCallback(callback: UserSwitchCallback) {
|
||||||
|
val interactorCallback = callbackCompatMap.remove(callback)
|
||||||
|
if (interactorCallback != null) {
|
||||||
|
userInteractor.removeCallback(interactorCallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Defines interface for classes that can be called back when the user is switched. */
|
fun dump(pw: PrintWriter, args: Array<out String>) {
|
||||||
fun interface UserSwitchCallback {
|
userInteractor.dump(pw)
|
||||||
/** Notifies that the user has switched. */
|
|
||||||
fun onUserSwitched()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -1,299 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.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.ui.LegacyUserUiHelper
|
|
||||||
import dagger.Lazy
|
|
||||||
import java.io.PrintWriter
|
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
/** Implementation of [UserSwitcherController]. */
|
|
||||||
@SysUISingleton
|
|
||||||
class UserSwitcherControllerImpl
|
|
||||||
@Inject
|
|
||||||
constructor(
|
|
||||||
@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 =
|
|
||||||
flags.isEnabled(Flags.USER_CONTROLLER_USES_INTERACTOR) &&
|
|
||||||
!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 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) {
|
|
||||||
userInteractor.userRecords.value
|
|
||||||
} else {
|
|
||||||
_oldImpl.users
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isSimpleUserSwitcher: Boolean
|
|
||||||
get() =
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.isSimpleUserSwitcher
|
|
||||||
} else {
|
|
||||||
_oldImpl.isSimpleUserSwitcher
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun init(view: View) {
|
|
||||||
if (!useInteractor) {
|
|
||||||
_oldImpl.init(view)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val currentUserRecord: UserRecord?
|
|
||||||
get() =
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.selectedUserRecord.value
|
|
||||||
} else {
|
|
||||||
_oldImpl.currentUserRecord
|
|
||||||
}
|
|
||||||
|
|
||||||
override val currentUserName: String?
|
|
||||||
get() =
|
|
||||||
if (useInteractor) {
|
|
||||||
currentUserRecord?.let {
|
|
||||||
LegacyUserUiHelper.getUserRecordName(
|
|
||||||
context = applicationContext,
|
|
||||||
record = it,
|
|
||||||
isGuestUserAutoCreated = userInteractor.isGuestUserAutoCreated,
|
|
||||||
isGuestUserResetting = userInteractor.isGuestUserResetting,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_oldImpl.currentUserName
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUserSelected(
|
|
||||||
userId: Int,
|
|
||||||
dialogShower: UserSwitchDialogController.DialogShower?
|
|
||||||
) {
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.selectUser(userId, dialogShower)
|
|
||||||
} else {
|
|
||||||
_oldImpl.onUserSelected(userId, dialogShower)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isAddUsersFromLockScreenEnabled: Flow<Boolean>
|
|
||||||
get() =
|
|
||||||
if (useInteractor) {
|
|
||||||
notSupported()
|
|
||||||
} else {
|
|
||||||
_oldImpl.isAddUsersFromLockScreenEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isGuestUserAutoCreated: Boolean
|
|
||||||
get() =
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.isGuestUserAutoCreated
|
|
||||||
} else {
|
|
||||||
_oldImpl.isGuestUserAutoCreated
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isGuestUserResetting: Boolean
|
|
||||||
get() =
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.isGuestUserResetting
|
|
||||||
} else {
|
|
||||||
_oldImpl.isGuestUserResetting
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createAndSwitchToGuestUser(
|
|
||||||
dialogShower: UserSwitchDialogController.DialogShower?,
|
|
||||||
) {
|
|
||||||
if (useInteractor) {
|
|
||||||
notSupported()
|
|
||||||
} else {
|
|
||||||
_oldImpl.createAndSwitchToGuestUser(dialogShower)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showAddUserDialog(dialogShower: UserSwitchDialogController.DialogShower?) {
|
|
||||||
if (useInteractor) {
|
|
||||||
notSupported()
|
|
||||||
} else {
|
|
||||||
_oldImpl.showAddUserDialog(dialogShower)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startSupervisedUserActivity() {
|
|
||||||
if (useInteractor) {
|
|
||||||
notSupported()
|
|
||||||
} else {
|
|
||||||
_oldImpl.startSupervisedUserActivity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDensityOrFontScaleChanged() {
|
|
||||||
if (!useInteractor) {
|
|
||||||
_oldImpl.onDensityOrFontScaleChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) {
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.addCallback(
|
|
||||||
object : UserInteractor.UserCallback {
|
|
||||||
override fun isEvictable(): Boolean {
|
|
||||||
return adapter.get() == null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUserStateChanged() {
|
|
||||||
adapter.get()?.notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
_oldImpl.addAdapter(adapter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUserListItemClicked(
|
|
||||||
record: UserRecord,
|
|
||||||
dialogShower: UserSwitchDialogController.DialogShower?,
|
|
||||||
) {
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.onRecordSelected(record, dialogShower)
|
|
||||||
} else {
|
|
||||||
_oldImpl.onUserListItemClicked(record, dialogShower)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeGuestUser(guestUserId: Int, targetUserId: Int) {
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.removeGuestUser(
|
|
||||||
guestUserId = guestUserId,
|
|
||||||
targetUserId = targetUserId,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
_oldImpl.removeGuestUser(guestUserId, targetUserId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun exitGuestUser(
|
|
||||||
guestUserId: Int,
|
|
||||||
targetUserId: Int,
|
|
||||||
forceRemoveGuestOnExit: Boolean
|
|
||||||
) {
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit)
|
|
||||||
} else {
|
|
||||||
_oldImpl.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun schedulePostBootGuestCreation() {
|
|
||||||
if (useInteractor) {
|
|
||||||
guestUserInteractor.onDeviceBootCompleted()
|
|
||||||
} else {
|
|
||||||
_oldImpl.schedulePostBootGuestCreation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isKeyguardShowing: Boolean
|
|
||||||
get() =
|
|
||||||
if (useInteractor) {
|
|
||||||
keyguardInteractor.isKeyguardShowing()
|
|
||||||
} else {
|
|
||||||
_oldImpl.isKeyguardShowing
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startActivity(intent: Intent) {
|
|
||||||
if (useInteractor) {
|
|
||||||
activityStarter.startActivity(intent, /* dismissShade= */ true)
|
|
||||||
} else {
|
|
||||||
_oldImpl.startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun refreshUsers(forcePictureLoadForId: Int) {
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.refreshUsers()
|
|
||||||
} else {
|
|
||||||
_oldImpl.refreshUsers(forcePictureLoadForId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) {
|
|
||||||
if (useInteractor) {
|
|
||||||
val interactorCallback =
|
|
||||||
object : UserInteractor.UserCallback {
|
|
||||||
override fun onUserStateChanged() {
|
|
||||||
callback.onUserSwitched()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callbackCompatMap[callback] = interactorCallback
|
|
||||||
userInteractor.addCallback(interactorCallback)
|
|
||||||
} else {
|
|
||||||
_oldImpl.addUserSwitchCallback(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) {
|
|
||||||
if (useInteractor) {
|
|
||||||
val interactorCallback = callbackCompatMap.remove(callback)
|
|
||||||
if (interactorCallback != null) {
|
|
||||||
userInteractor.removeCallback(interactorCallback)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_oldImpl.removeUserSwitchCallback(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
|
||||||
if (useInteractor) {
|
|
||||||
userInteractor.dump(pw)
|
|
||||||
} else {
|
|
||||||
_oldImpl.dump(pw, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -58,8 +58,6 @@ import com.android.systemui.statusbar.policy.SecurityController;
|
|||||||
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
|
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
|
||||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||||
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
|
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherControllerImpl;
|
|
||||||
import com.android.systemui.statusbar.policy.WalletController;
|
import com.android.systemui.statusbar.policy.WalletController;
|
||||||
import com.android.systemui.statusbar.policy.WalletControllerImpl;
|
import com.android.systemui.statusbar.policy.WalletControllerImpl;
|
||||||
import com.android.systemui.statusbar.policy.ZenModeController;
|
import com.android.systemui.statusbar.policy.ZenModeController;
|
||||||
@@ -198,8 +196,4 @@ public interface StatusBarPolicyModule {
|
|||||||
static DataSaverController provideDataSaverController(NetworkController networkController) {
|
static DataSaverController provideDataSaverController(NetworkController networkController) {
|
||||||
return networkController.getDataSaverController();
|
return networkController.getDataSaverController();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Binds {@link UserSwitcherController} to its implementation. */
|
|
||||||
@Binds
|
|
||||||
UserSwitcherController bindUserSwitcherController(UserSwitcherControllerImpl impl);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,31 +19,18 @@ package com.android.systemui.user.data.repository
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.UserInfo
|
import android.content.pm.UserInfo
|
||||||
import android.graphics.drawable.BitmapDrawable
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.os.UserHandle
|
import android.os.UserHandle
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
|
||||||
import com.android.internal.util.UserIcons
|
|
||||||
import com.android.systemui.R
|
|
||||||
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
|
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
|
||||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||||
import com.android.systemui.common.shared.model.Text
|
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.dagger.qualifiers.Application
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
import com.android.systemui.dagger.qualifiers.Background
|
import com.android.systemui.dagger.qualifiers.Background
|
||||||
import com.android.systemui.dagger.qualifiers.Main
|
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.settings.UserTracker
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
|
||||||
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
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.GlobalSettings
|
||||||
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
|
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
@@ -55,7 +42,6 @@ import kotlinx.coroutines.channels.awaitClose
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
@@ -72,15 +58,6 @@ import kotlinx.coroutines.withContext
|
|||||||
* upstream changes.
|
* upstream changes.
|
||||||
*/
|
*/
|
||||||
interface UserRepository {
|
interface UserRepository {
|
||||||
/** List of all users on the device. */
|
|
||||||
val users: Flow<List<UserModel>>
|
|
||||||
|
|
||||||
/** The currently-selected user. */
|
|
||||||
val selectedUser: Flow<UserModel>
|
|
||||||
|
|
||||||
/** List of available user-related actions. */
|
|
||||||
val actions: Flow<List<UserActionModel>>
|
|
||||||
|
|
||||||
/** User switcher related settings. */
|
/** User switcher related settings. */
|
||||||
val userSwitcherSettings: Flow<UserSwitcherSettingsModel>
|
val userSwitcherSettings: Flow<UserSwitcherSettingsModel>
|
||||||
|
|
||||||
@@ -93,9 +70,6 @@ interface UserRepository {
|
|||||||
/** User ID of the last non-guest selected user. */
|
/** User ID of the last non-guest selected user. */
|
||||||
val lastSelectedNonGuestUserId: Int
|
val lastSelectedNonGuestUserId: Int
|
||||||
|
|
||||||
/** Whether actions are available even when locked. */
|
|
||||||
val isActionableWhenLocked: Flow<Boolean>
|
|
||||||
|
|
||||||
/** Whether the device is configured to always have a guest user available. */
|
/** Whether the device is configured to always have a guest user available. */
|
||||||
val isGuestUserAutoCreated: Boolean
|
val isGuestUserAutoCreated: Boolean
|
||||||
|
|
||||||
@@ -125,18 +99,13 @@ class UserRepositoryImpl
|
|||||||
constructor(
|
constructor(
|
||||||
@Application private val appContext: Context,
|
@Application private val appContext: Context,
|
||||||
private val manager: UserManager,
|
private val manager: UserManager,
|
||||||
private val controller: UserSwitcherController,
|
|
||||||
@Application private val applicationScope: CoroutineScope,
|
@Application private val applicationScope: CoroutineScope,
|
||||||
@Main private val mainDispatcher: CoroutineDispatcher,
|
@Main private val mainDispatcher: CoroutineDispatcher,
|
||||||
@Background private val backgroundDispatcher: CoroutineDispatcher,
|
@Background private val backgroundDispatcher: CoroutineDispatcher,
|
||||||
private val globalSettings: GlobalSettings,
|
private val globalSettings: GlobalSettings,
|
||||||
private val tracker: UserTracker,
|
private val tracker: UserTracker,
|
||||||
private val featureFlags: FeatureFlags,
|
|
||||||
) : UserRepository {
|
) : UserRepository {
|
||||||
|
|
||||||
private val isNewImpl: Boolean
|
|
||||||
get() = !featureFlags.isEnabled(Flags.USER_INTERACTOR_AND_REPO_USE_CONTROLLER)
|
|
||||||
|
|
||||||
private val _userSwitcherSettings = MutableStateFlow(runBlocking { getSettings() })
|
private val _userSwitcherSettings = MutableStateFlow(runBlocking { getSettings() })
|
||||||
override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> =
|
override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> =
|
||||||
_userSwitcherSettings.asStateFlow().filterNotNull()
|
_userSwitcherSettings.asStateFlow().filterNotNull()
|
||||||
@@ -150,58 +119,11 @@ constructor(
|
|||||||
override var lastSelectedNonGuestUserId: Int = UserHandle.USER_SYSTEM
|
override var lastSelectedNonGuestUserId: Int = UserHandle.USER_SYSTEM
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private val userRecords: Flow<List<UserRecord>> = conflatedCallbackFlow {
|
|
||||||
fun send() {
|
|
||||||
trySendWithFailureLogging(
|
|
||||||
controller.users,
|
|
||||||
TAG,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val callback = UserSwitcherController.UserSwitchCallback { send() }
|
|
||||||
|
|
||||||
controller.addUserSwitchCallback(callback)
|
|
||||||
send()
|
|
||||||
|
|
||||||
awaitClose { controller.removeUserSwitchCallback(callback) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override val users: Flow<List<UserModel>> =
|
|
||||||
userRecords.map { records -> records.filter { it.isUser() }.map { it.toUserModel() } }
|
|
||||||
|
|
||||||
override val selectedUser: Flow<UserModel> =
|
|
||||||
users.map { users -> users.first { user -> user.isSelected } }
|
|
||||||
|
|
||||||
override val actions: Flow<List<UserActionModel>> =
|
|
||||||
userRecords.map { records -> records.filter { it.isNotUser() }.map { it.toActionModel() } }
|
|
||||||
|
|
||||||
override val isActionableWhenLocked: Flow<Boolean> =
|
|
||||||
if (isNewImpl) {
|
|
||||||
emptyFlow()
|
|
||||||
} else {
|
|
||||||
controller.isAddUsersFromLockScreenEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
override val isGuestUserAutoCreated: Boolean =
|
override val isGuestUserAutoCreated: Boolean =
|
||||||
if (isNewImpl) {
|
|
||||||
appContext.resources.getBoolean(com.android.internal.R.bool.config_guestUserAutoCreated)
|
appContext.resources.getBoolean(com.android.internal.R.bool.config_guestUserAutoCreated)
|
||||||
} else {
|
|
||||||
controller.isGuestUserAutoCreated
|
|
||||||
}
|
|
||||||
|
|
||||||
private var _isGuestUserResetting: Boolean = false
|
private var _isGuestUserResetting: Boolean = false
|
||||||
override var isGuestUserResetting: Boolean =
|
override var isGuestUserResetting: Boolean = _isGuestUserResetting
|
||||||
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 val isGuestUserCreationScheduled = AtomicBoolean()
|
||||||
|
|
||||||
@@ -210,11 +132,9 @@ constructor(
|
|||||||
override var isRefreshUsersPaused: Boolean = false
|
override var isRefreshUsersPaused: Boolean = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (isNewImpl) {
|
|
||||||
observeSelectedUser()
|
observeSelectedUser()
|
||||||
observeUserSettings()
|
observeUserSettings()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun refreshUsers() {
|
override fun refreshUsers() {
|
||||||
applicationScope.launch {
|
applicationScope.launch {
|
||||||
@@ -327,64 +247,6 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun UserRecord.isUser(): Boolean {
|
|
||||||
return when {
|
|
||||||
isAddUser -> false
|
|
||||||
isAddSupervisedUser -> false
|
|
||||||
isManageUsers -> false
|
|
||||||
isGuest -> info != null
|
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun UserRecord.isNotUser(): Boolean {
|
|
||||||
return !isUser()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun UserRecord.toUserModel(): UserModel {
|
|
||||||
return UserModel(
|
|
||||||
id = resolveId(),
|
|
||||||
name = getUserName(this),
|
|
||||||
image = getUserImage(this),
|
|
||||||
isSelected = isCurrent,
|
|
||||||
isSelectable = isSwitchToEnabled || isGuest,
|
|
||||||
isGuest = isGuest,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun UserRecord.toActionModel(): UserActionModel {
|
|
||||||
return when {
|
|
||||||
isAddUser -> UserActionModel.ADD_USER
|
|
||||||
isAddSupervisedUser -> UserActionModel.ADD_SUPERVISED_USER
|
|
||||||
isGuest -> UserActionModel.ENTER_GUEST_MODE
|
|
||||||
isManageUsers -> UserActionModel.NAVIGATE_TO_USER_MANAGEMENT
|
|
||||||
else -> error("Don't know how to convert to UserActionModel: $this")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getUserName(record: UserRecord): Text {
|
|
||||||
val resourceId: Int? = LegacyUserUiHelper.getGuestUserRecordNameResourceId(record)
|
|
||||||
return if (resourceId != null) {
|
|
||||||
Text.Resource(resourceId)
|
|
||||||
} else {
|
|
||||||
Text.Loaded(checkNotNull(record.info).name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getUserImage(record: UserRecord): Drawable {
|
|
||||||
if (record.isGuest) {
|
|
||||||
return checkNotNull(
|
|
||||||
AppCompatResources.getDrawable(appContext, R.drawable.ic_account_circle)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val userId = checkNotNull(record.info?.id)
|
|
||||||
return manager.getUserIcon(userId)?.let { userSelectedIcon ->
|
|
||||||
BitmapDrawable(userSelectedIcon)
|
|
||||||
}
|
|
||||||
?: UserIcons.getDefaultUserIcon(appContext.resources, userId, /* light= */ false)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "UserRepository"
|
private const val TAG = "UserRepository"
|
||||||
@VisibleForTesting const val SETTING_SIMPLE_USER_SWITCHER = "lockscreenSimpleUserSwitcher"
|
@VisibleForTesting const val SETTING_SIMPLE_USER_SWITCHER = "lockscreenSimpleUserSwitcher"
|
||||||
|
|||||||
@@ -39,12 +39,9 @@ import com.android.systemui.common.shared.model.Text
|
|||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.dagger.qualifiers.Application
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
import com.android.systemui.dagger.qualifiers.Background
|
import com.android.systemui.dagger.qualifiers.Background
|
||||||
import com.android.systemui.flags.FeatureFlags
|
|
||||||
import com.android.systemui.flags.Flags
|
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.qs.user.UserSwitchDialogController
|
import com.android.systemui.qs.user.UserSwitchDialogController
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
|
||||||
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
|
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
|
||||||
import com.android.systemui.user.data.repository.UserRepository
|
import com.android.systemui.user.data.repository.UserRepository
|
||||||
import com.android.systemui.user.data.source.UserRecord
|
import com.android.systemui.user.data.source.UserRecord
|
||||||
@@ -64,8 +61,6 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
|
||||||
import kotlinx.coroutines.flow.flowOf
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
@@ -82,10 +77,8 @@ class UserInteractor
|
|||||||
constructor(
|
constructor(
|
||||||
@Application private val applicationContext: Context,
|
@Application private val applicationContext: Context,
|
||||||
private val repository: UserRepository,
|
private val repository: UserRepository,
|
||||||
private val controller: UserSwitcherController,
|
|
||||||
private val activityStarter: ActivityStarter,
|
private val activityStarter: ActivityStarter,
|
||||||
private val keyguardInteractor: KeyguardInteractor,
|
private val keyguardInteractor: KeyguardInteractor,
|
||||||
private val featureFlags: FeatureFlags,
|
|
||||||
private val manager: UserManager,
|
private val manager: UserManager,
|
||||||
@Application private val applicationScope: CoroutineScope,
|
@Application private val applicationScope: CoroutineScope,
|
||||||
telephonyInteractor: TelephonyInteractor,
|
telephonyInteractor: TelephonyInteractor,
|
||||||
@@ -107,9 +100,6 @@ constructor(
|
|||||||
fun onUserStateChanged()
|
fun onUserStateChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val isNewImpl: Boolean
|
|
||||||
get() = !featureFlags.isEnabled(Flags.USER_INTERACTOR_AND_REPO_USE_CONTROLLER)
|
|
||||||
|
|
||||||
private val supervisedUserPackageName: String?
|
private val supervisedUserPackageName: String?
|
||||||
get() =
|
get() =
|
||||||
applicationContext.getString(
|
applicationContext.getString(
|
||||||
@@ -122,7 +112,6 @@ constructor(
|
|||||||
/** List of current on-device users to select from. */
|
/** List of current on-device users to select from. */
|
||||||
val users: Flow<List<UserModel>>
|
val users: Flow<List<UserModel>>
|
||||||
get() =
|
get() =
|
||||||
if (isNewImpl) {
|
|
||||||
combine(
|
combine(
|
||||||
repository.userInfos,
|
repository.userInfos,
|
||||||
repository.selectedUserInfo,
|
repository.selectedUserInfo,
|
||||||
@@ -134,14 +123,10 @@ constructor(
|
|||||||
isUserSwitcherEnabled = settings.isUserSwitcherEnabled,
|
isUserSwitcherEnabled = settings.isUserSwitcherEnabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
repository.users
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The currently-selected user. */
|
/** The currently-selected user. */
|
||||||
val selectedUser: Flow<UserModel>
|
val selectedUser: Flow<UserModel>
|
||||||
get() =
|
get() =
|
||||||
if (isNewImpl) {
|
|
||||||
combine(
|
combine(
|
||||||
repository.selectedUserInfo,
|
repository.selectedUserInfo,
|
||||||
repository.userSwitcherSettings,
|
repository.userSwitcherSettings,
|
||||||
@@ -156,14 +141,10 @@ constructor(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
repository.selectedUser
|
|
||||||
}
|
|
||||||
|
|
||||||
/** List of user-switcher related actions that are available. */
|
/** List of user-switcher related actions that are available. */
|
||||||
val actions: Flow<List<UserActionModel>>
|
val actions: Flow<List<UserActionModel>>
|
||||||
get() =
|
get() =
|
||||||
if (isNewImpl) {
|
|
||||||
combine(
|
combine(
|
||||||
repository.selectedUserInfo,
|
repository.selectedUserInfo,
|
||||||
repository.userInfos,
|
repository.userInfos,
|
||||||
@@ -227,27 +208,8 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
combine(
|
|
||||||
repository.isActionableWhenLocked,
|
|
||||||
keyguardInteractor.isKeyguardShowing,
|
|
||||||
) { isActionableWhenLocked, isLocked ->
|
|
||||||
isActionableWhenLocked || !isLocked
|
|
||||||
}
|
|
||||||
.flatMapLatest { isActionable ->
|
|
||||||
if (isActionable) {
|
|
||||||
repository.actions
|
|
||||||
} 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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val userRecords: StateFlow<ArrayList<UserRecord>> =
|
val userRecords: StateFlow<ArrayList<UserRecord>> =
|
||||||
if (isNewImpl) {
|
|
||||||
combine(
|
combine(
|
||||||
repository.userInfos,
|
repository.userInfos,
|
||||||
repository.selectedUserInfo,
|
repository.selectedUserInfo,
|
||||||
@@ -279,12 +241,8 @@ constructor(
|
|||||||
started = SharingStarted.Eagerly,
|
started = SharingStarted.Eagerly,
|
||||||
initialValue = ArrayList(),
|
initialValue = ArrayList(),
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
MutableStateFlow(ArrayList())
|
|
||||||
}
|
|
||||||
|
|
||||||
val selectedUserRecord: StateFlow<UserRecord?> =
|
val selectedUserRecord: StateFlow<UserRecord?> =
|
||||||
if (isNewImpl) {
|
|
||||||
repository.selectedUserInfo
|
repository.selectedUserInfo
|
||||||
.map { selectedUserInfo ->
|
.map { selectedUserInfo ->
|
||||||
toRecord(userInfo = selectedUserInfo, selectedUserId = selectedUserInfo.id)
|
toRecord(userInfo = selectedUserInfo, selectedUserId = selectedUserInfo.id)
|
||||||
@@ -294,9 +252,6 @@ constructor(
|
|||||||
started = SharingStarted.Eagerly,
|
started = SharingStarted.Eagerly,
|
||||||
initialValue = null,
|
initialValue = null,
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
MutableStateFlow(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Whether the device is configured to always have a guest user available. */
|
/** Whether the device is configured to always have a guest user available. */
|
||||||
val isGuestUserAutoCreated: Boolean = guestUserInteractor.isGuestUserAutoCreated
|
val isGuestUserAutoCreated: Boolean = guestUserInteractor.isGuestUserAutoCreated
|
||||||
@@ -311,15 +266,9 @@ constructor(
|
|||||||
val dialogDismissRequests: Flow<Unit?> = _dialogDismissRequests.asStateFlow()
|
val dialogDismissRequests: Flow<Unit?> = _dialogDismissRequests.asStateFlow()
|
||||||
|
|
||||||
val isSimpleUserSwitcher: Boolean
|
val isSimpleUserSwitcher: Boolean
|
||||||
get() =
|
get() = repository.isSimpleUserSwitcher()
|
||||||
if (isNewImpl) {
|
|
||||||
repository.isSimpleUserSwitcher()
|
|
||||||
} else {
|
|
||||||
error("Not supported in the old implementation!")
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (isNewImpl) {
|
|
||||||
refreshUsersScheduler.refreshIfNotPaused()
|
refreshUsersScheduler.refreshIfNotPaused()
|
||||||
telephonyInteractor.callState
|
telephonyInteractor.callState
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
@@ -349,7 +298,6 @@ constructor(
|
|||||||
}
|
}
|
||||||
.launchIn(applicationScope)
|
.launchIn(applicationScope)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun addCallback(callback: UserCallback) {
|
fun addCallback(callback: UserCallback) {
|
||||||
applicationScope.launch { callbackMutex.withLock { callbacks.add(callback) } }
|
applicationScope.launch { callbackMutex.withLock { callbacks.add(callback) } }
|
||||||
@@ -414,11 +362,9 @@ constructor(
|
|||||||
newlySelectedUserId: Int,
|
newlySelectedUserId: Int,
|
||||||
dialogShower: UserSwitchDialogController.DialogShower? = null,
|
dialogShower: UserSwitchDialogController.DialogShower? = null,
|
||||||
) {
|
) {
|
||||||
if (isNewImpl) {
|
|
||||||
val currentlySelectedUserInfo = repository.getSelectedUserInfo()
|
val currentlySelectedUserInfo = repository.getSelectedUserInfo()
|
||||||
if (
|
if (
|
||||||
newlySelectedUserId == currentlySelectedUserInfo.id &&
|
newlySelectedUserId == currentlySelectedUserInfo.id && currentlySelectedUserInfo.isGuest
|
||||||
currentlySelectedUserInfo.isGuest
|
|
||||||
) {
|
) {
|
||||||
// Here when clicking on the currently-selected guest user to leave guest mode
|
// Here when clicking on the currently-selected guest user to leave guest mode
|
||||||
// and return to the previously-selected non-guest user.
|
// and return to the previously-selected non-guest user.
|
||||||
@@ -453,9 +399,6 @@ constructor(
|
|||||||
dialogShower?.dismiss()
|
dialogShower?.dismiss()
|
||||||
|
|
||||||
switchUser(newlySelectedUserId)
|
switchUser(newlySelectedUserId)
|
||||||
} else {
|
|
||||||
controller.onUserSelected(newlySelectedUserId, dialogShower)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Executes the given action. */
|
/** Executes the given action. */
|
||||||
@@ -463,7 +406,6 @@ constructor(
|
|||||||
action: UserActionModel,
|
action: UserActionModel,
|
||||||
dialogShower: UserSwitchDialogController.DialogShower? = null,
|
dialogShower: UserSwitchDialogController.DialogShower? = null,
|
||||||
) {
|
) {
|
||||||
if (isNewImpl) {
|
|
||||||
when (action) {
|
when (action) {
|
||||||
UserActionModel.ENTER_GUEST_MODE ->
|
UserActionModel.ENTER_GUEST_MODE ->
|
||||||
guestUserInteractor.createAndSwitchTo(
|
guestUserInteractor.createAndSwitchTo(
|
||||||
@@ -497,18 +439,6 @@ constructor(
|
|||||||
/* dismissShade= */ true,
|
/* dismissShade= */ true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exitGuestUser(
|
fun exitGuestUser(
|
||||||
|
|||||||
@@ -27,15 +27,12 @@ import com.android.systemui.animation.DialogLaunchAnimator
|
|||||||
import com.android.systemui.broadcast.BroadcastSender
|
import com.android.systemui.broadcast.BroadcastSender
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.dagger.qualifiers.Application
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
import com.android.systemui.flags.FeatureFlags
|
|
||||||
import com.android.systemui.flags.Flags
|
|
||||||
import com.android.systemui.plugins.FalsingManager
|
import com.android.systemui.plugins.FalsingManager
|
||||||
import com.android.systemui.user.domain.interactor.UserInteractor
|
import com.android.systemui.user.domain.interactor.UserInteractor
|
||||||
import com.android.systemui.user.domain.model.ShowDialogRequestModel
|
import com.android.systemui.user.domain.model.ShowDialogRequestModel
|
||||||
import dagger.Lazy
|
import dagger.Lazy
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -50,16 +47,11 @@ constructor(
|
|||||||
private val broadcastSender: Lazy<BroadcastSender>,
|
private val broadcastSender: Lazy<BroadcastSender>,
|
||||||
private val dialogLaunchAnimator: Lazy<DialogLaunchAnimator>,
|
private val dialogLaunchAnimator: Lazy<DialogLaunchAnimator>,
|
||||||
private val interactor: Lazy<UserInteractor>,
|
private val interactor: Lazy<UserInteractor>,
|
||||||
private val featureFlags: Lazy<FeatureFlags>,
|
|
||||||
) : CoreStartable {
|
) : CoreStartable {
|
||||||
|
|
||||||
private var currentDialog: Dialog? = null
|
private var currentDialog: Dialog? = null
|
||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
if (featureFlags.get().isEnabled(Flags.USER_INTERACTOR_AND_REPO_USE_CONTROLLER)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
startHandlingDialogShowRequests()
|
startHandlingDialogShowRequests()
|
||||||
startHandlingDialogDismissRequests()
|
startHandlingDialogDismissRequests()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ package com.android.systemui.user.ui.viewmodel
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.android.systemui.common.ui.drawable.CircularDrawable
|
import com.android.systemui.common.ui.drawable.CircularDrawable
|
||||||
import com.android.systemui.flags.FeatureFlags
|
|
||||||
import com.android.systemui.flags.Flags
|
|
||||||
import com.android.systemui.power.domain.interactor.PowerInteractor
|
import com.android.systemui.power.domain.interactor.PowerInteractor
|
||||||
import com.android.systemui.user.domain.interactor.GuestUserInteractor
|
import com.android.systemui.user.domain.interactor.GuestUserInteractor
|
||||||
import com.android.systemui.user.domain.interactor.UserInteractor
|
import com.android.systemui.user.domain.interactor.UserInteractor
|
||||||
@@ -41,12 +39,8 @@ private constructor(
|
|||||||
private val userInteractor: UserInteractor,
|
private val userInteractor: UserInteractor,
|
||||||
private val guestUserInteractor: GuestUserInteractor,
|
private val guestUserInteractor: GuestUserInteractor,
|
||||||
private val powerInteractor: PowerInteractor,
|
private val powerInteractor: PowerInteractor,
|
||||||
private val featureFlags: FeatureFlags,
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val isNewImpl: Boolean
|
|
||||||
get() = !featureFlags.isEnabled(Flags.USER_INTERACTOR_AND_REPO_USE_CONTROLLER)
|
|
||||||
|
|
||||||
/** On-device users. */
|
/** On-device users. */
|
||||||
val users: Flow<List<UserViewModel>> =
|
val users: Flow<List<UserViewModel>> =
|
||||||
userInteractor.users.map { models -> models.map { user -> toViewModel(user) } }
|
userInteractor.users.map { models -> models.map { user -> toViewModel(user) } }
|
||||||
@@ -216,7 +210,6 @@ private constructor(
|
|||||||
private val userInteractor: UserInteractor,
|
private val userInteractor: UserInteractor,
|
||||||
private val guestUserInteractor: GuestUserInteractor,
|
private val guestUserInteractor: GuestUserInteractor,
|
||||||
private val powerInteractor: PowerInteractor,
|
private val powerInteractor: PowerInteractor,
|
||||||
private val featureFlags: FeatureFlags,
|
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -224,7 +217,6 @@ private constructor(
|
|||||||
userInteractor = userInteractor,
|
userInteractor = userInteractor,
|
||||||
guestUserInteractor = guestUserInteractor,
|
guestUserInteractor = guestUserInteractor,
|
||||||
powerInteractor = powerInteractor,
|
powerInteractor = powerInteractor,
|
||||||
featureFlags = featureFlags,
|
|
||||||
)
|
)
|
||||||
as T
|
as T
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,27 +42,21 @@ import org.mockito.ArgumentMatchers.any
|
|||||||
import org.mockito.ArgumentMatchers.anyBoolean
|
import org.mockito.ArgumentMatchers.anyBoolean
|
||||||
import org.mockito.ArgumentMatchers.anyInt
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.`when`
|
|
||||||
import org.mockito.Mockito.mock
|
import org.mockito.Mockito.mock
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.Mockito.`when`
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
@RunWith(AndroidTestingRunner::class)
|
@RunWith(AndroidTestingRunner::class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
class UserDetailViewAdapterTest : SysuiTestCase() {
|
class UserDetailViewAdapterTest : SysuiTestCase() {
|
||||||
|
|
||||||
@Mock
|
@Mock private lateinit var mUserSwitcherController: UserSwitcherController
|
||||||
private lateinit var mUserSwitcherController: UserSwitcherController
|
@Mock private lateinit var mParent: ViewGroup
|
||||||
@Mock
|
@Mock private lateinit var mUserDetailItemView: UserDetailItemView
|
||||||
private lateinit var mParent: ViewGroup
|
@Mock private lateinit var mOtherView: View
|
||||||
@Mock
|
@Mock private lateinit var mInflatedUserDetailItemView: UserDetailItemView
|
||||||
private lateinit var mUserDetailItemView: UserDetailItemView
|
@Mock private lateinit var mLayoutInflater: LayoutInflater
|
||||||
@Mock
|
|
||||||
private lateinit var mOtherView: View
|
|
||||||
@Mock
|
|
||||||
private lateinit var mInflatedUserDetailItemView: UserDetailItemView
|
|
||||||
@Mock
|
|
||||||
private lateinit var mLayoutInflater: LayoutInflater
|
|
||||||
private var falsingManagerFake: FalsingManagerFake = FalsingManagerFake()
|
private var falsingManagerFake: FalsingManagerFake = FalsingManagerFake()
|
||||||
private lateinit var adapter: UserDetailView.Adapter
|
private lateinit var adapter: UserDetailView.Adapter
|
||||||
private lateinit var uiEventLogger: UiEventLoggerFake
|
private lateinit var uiEventLogger: UiEventLoggerFake
|
||||||
@@ -77,8 +71,11 @@ class UserDetailViewAdapterTest : SysuiTestCase() {
|
|||||||
`when`(mLayoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean()))
|
`when`(mLayoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean()))
|
||||||
.thenReturn(mInflatedUserDetailItemView)
|
.thenReturn(mInflatedUserDetailItemView)
|
||||||
`when`(mParent.context).thenReturn(mContext)
|
`when`(mParent.context).thenReturn(mContext)
|
||||||
adapter = UserDetailView.Adapter(
|
adapter =
|
||||||
mContext, mUserSwitcherController, uiEventLogger,
|
UserDetailView.Adapter(
|
||||||
|
mContext,
|
||||||
|
mUserSwitcherController,
|
||||||
|
uiEventLogger,
|
||||||
falsingManagerFake
|
falsingManagerFake
|
||||||
)
|
)
|
||||||
mPicture = UserIcons.convertToBitmap(mContext.getDrawable(R.drawable.ic_avatar_user))
|
mPicture = UserIcons.convertToBitmap(mContext.getDrawable(R.drawable.ic_avatar_user))
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ class BaseUserSwitcherAdapterTest : SysuiTestCase() {
|
|||||||
fun refresh() {
|
fun refresh() {
|
||||||
underTest.refresh()
|
underTest.refresh()
|
||||||
|
|
||||||
verify(controller).refreshUsers(UserHandle.USER_NULL)
|
verify(controller).refreshUsers()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createUserRecord(
|
private fun createUserRecord(
|
||||||
|
|||||||
@@ -1,727 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2021 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.statusbar.policy
|
|
||||||
|
|
||||||
import android.app.IActivityManager
|
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.admin.DevicePolicyManager
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.UserInfo
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.hardware.face.FaceManager
|
|
||||||
import android.hardware.fingerprint.FingerprintManager
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.UserHandle
|
|
||||||
import android.os.UserManager
|
|
||||||
import android.provider.Settings
|
|
||||||
import android.testing.AndroidTestingRunner
|
|
||||||
import android.testing.TestableLooper
|
|
||||||
import android.view.ThreadedRenderer
|
|
||||||
import androidx.test.filters.SmallTest
|
|
||||||
import com.android.internal.jank.InteractionJankMonitor
|
|
||||||
import com.android.internal.logging.testing.UiEventLoggerFake
|
|
||||||
import com.android.internal.util.LatencyTracker
|
|
||||||
import com.android.internal.util.UserIcons
|
|
||||||
import com.android.systemui.GuestResetOrExitSessionReceiver
|
|
||||||
import com.android.systemui.GuestResumeSessionReceiver
|
|
||||||
import com.android.systemui.GuestSessionNotification
|
|
||||||
import com.android.systemui.R
|
|
||||||
import com.android.systemui.SysuiTestCase
|
|
||||||
import com.android.systemui.animation.DialogCuj
|
|
||||||
import com.android.systemui.animation.DialogLaunchAnimator
|
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
|
||||||
import com.android.systemui.broadcast.BroadcastSender
|
|
||||||
import com.android.systemui.dump.DumpManager
|
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
|
||||||
import com.android.systemui.plugins.FalsingManager
|
|
||||||
import com.android.systemui.qs.QSUserSwitcherEvent
|
|
||||||
import com.android.systemui.qs.user.UserSwitchDialogController
|
|
||||||
import com.android.systemui.settings.UserTracker
|
|
||||||
import com.android.systemui.shade.NotificationShadeWindowView
|
|
||||||
import com.android.systemui.telephony.TelephonyListenerManager
|
|
||||||
import com.android.systemui.user.data.source.UserRecord
|
|
||||||
import com.android.systemui.user.legacyhelper.data.LegacyUserDataHelper
|
|
||||||
import com.android.systemui.user.shared.model.UserActionModel
|
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor
|
|
||||||
import com.android.systemui.util.mockito.any
|
|
||||||
import com.android.systemui.util.mockito.argumentCaptor
|
|
||||||
import com.android.systemui.util.mockito.capture
|
|
||||||
import com.android.systemui.util.mockito.kotlinArgumentCaptor
|
|
||||||
import com.android.systemui.util.mockito.nullable
|
|
||||||
import com.android.systemui.util.settings.GlobalSettings
|
|
||||||
import com.android.systemui.util.settings.SecureSettings
|
|
||||||
import com.android.systemui.util.time.FakeSystemClock
|
|
||||||
import com.google.common.truth.Truth
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Assert.assertFalse
|
|
||||||
import org.junit.Assert.assertNotNull
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mockito.ArgumentMatchers.anyInt
|
|
||||||
import org.mockito.Mock
|
|
||||||
import org.mockito.Mockito.doNothing
|
|
||||||
import org.mockito.Mockito.doReturn
|
|
||||||
import org.mockito.Mockito.eq
|
|
||||||
import org.mockito.Mockito.mock
|
|
||||||
import org.mockito.Mockito.never
|
|
||||||
import org.mockito.Mockito.verify
|
|
||||||
import org.mockito.Mockito.`when`
|
|
||||||
import org.mockito.MockitoAnnotations
|
|
||||||
|
|
||||||
@RunWith(AndroidTestingRunner::class)
|
|
||||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
|
||||||
@SmallTest
|
|
||||||
class UserSwitcherControllerOldImplTest : SysuiTestCase() {
|
|
||||||
@Mock private lateinit var keyguardStateController: KeyguardStateController
|
|
||||||
@Mock private lateinit var activityManager: IActivityManager
|
|
||||||
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
|
|
||||||
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
|
|
||||||
@Mock private lateinit var handler: Handler
|
|
||||||
@Mock private lateinit var userTracker: UserTracker
|
|
||||||
@Mock private lateinit var userManager: UserManager
|
|
||||||
@Mock private lateinit var activityStarter: ActivityStarter
|
|
||||||
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
|
|
||||||
@Mock private lateinit var broadcastSender: BroadcastSender
|
|
||||||
@Mock private lateinit var telephonyListenerManager: TelephonyListenerManager
|
|
||||||
@Mock private lateinit var secureSettings: SecureSettings
|
|
||||||
@Mock private lateinit var falsingManager: FalsingManager
|
|
||||||
@Mock private lateinit var dumpManager: DumpManager
|
|
||||||
@Mock private lateinit var interactionJankMonitor: InteractionJankMonitor
|
|
||||||
@Mock private lateinit var latencyTracker: LatencyTracker
|
|
||||||
@Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower
|
|
||||||
@Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
|
|
||||||
@Mock private lateinit var threadedRenderer: ThreadedRenderer
|
|
||||||
@Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
|
|
||||||
@Mock private lateinit var globalSettings: GlobalSettings
|
|
||||||
@Mock private lateinit var guestSessionNotification: GuestSessionNotification
|
|
||||||
@Mock private lateinit var guestResetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
|
|
||||||
private lateinit var resetSessionDialogFactory:
|
|
||||||
GuestResumeSessionReceiver.ResetSessionDialog.Factory
|
|
||||||
private lateinit var guestResumeSessionReceiver: GuestResumeSessionReceiver
|
|
||||||
private lateinit var testableLooper: TestableLooper
|
|
||||||
private lateinit var bgExecutor: FakeExecutor
|
|
||||||
private lateinit var longRunningExecutor: FakeExecutor
|
|
||||||
private lateinit var uiExecutor: FakeExecutor
|
|
||||||
private lateinit var uiEventLogger: UiEventLoggerFake
|
|
||||||
private lateinit var userSwitcherController: UserSwitcherControllerOldImpl
|
|
||||||
private lateinit var picture: Bitmap
|
|
||||||
private val ownerId = UserHandle.USER_SYSTEM
|
|
||||||
private val ownerInfo = UserInfo(ownerId, "Owner", null,
|
|
||||||
UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL or UserInfo.FLAG_INITIALIZED or
|
|
||||||
UserInfo.FLAG_PRIMARY or UserInfo.FLAG_SYSTEM or UserInfo.FLAG_ADMIN,
|
|
||||||
UserManager.USER_TYPE_FULL_SYSTEM)
|
|
||||||
private val guestId = 1234
|
|
||||||
private val guestInfo = UserInfo(guestId, "Guest", null,
|
|
||||||
UserInfo.FLAG_FULL or UserInfo.FLAG_GUEST, UserManager.USER_TYPE_FULL_GUEST)
|
|
||||||
private val secondaryUser =
|
|
||||||
UserInfo(10, "Secondary", null, 0, UserManager.USER_TYPE_FULL_SECONDARY)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this)
|
|
||||||
testableLooper = TestableLooper.get(this)
|
|
||||||
bgExecutor = FakeExecutor(FakeSystemClock())
|
|
||||||
longRunningExecutor = FakeExecutor(FakeSystemClock())
|
|
||||||
uiExecutor = FakeExecutor(FakeSystemClock())
|
|
||||||
uiEventLogger = UiEventLoggerFake()
|
|
||||||
|
|
||||||
mContext.orCreateTestableResources.addOverride(
|
|
||||||
com.android.internal.R.bool.config_guestUserAutoCreated, false)
|
|
||||||
|
|
||||||
mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java))
|
|
||||||
mContext.addMockSystemService(Context.NOTIFICATION_SERVICE,
|
|
||||||
mock(NotificationManager::class.java))
|
|
||||||
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE,
|
|
||||||
mock(FingerprintManager::class.java))
|
|
||||||
|
|
||||||
resetSessionDialogFactory = object : GuestResumeSessionReceiver.ResetSessionDialog.Factory {
|
|
||||||
override fun create(userId: Int): GuestResumeSessionReceiver.ResetSessionDialog {
|
|
||||||
return GuestResumeSessionReceiver.ResetSessionDialog(
|
|
||||||
mContext,
|
|
||||||
mock(UserSwitcherController::class.java),
|
|
||||||
uiEventLogger,
|
|
||||||
userId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guestResumeSessionReceiver = GuestResumeSessionReceiver(userTracker,
|
|
||||||
secureSettings,
|
|
||||||
broadcastDispatcher,
|
|
||||||
guestSessionNotification,
|
|
||||||
resetSessionDialogFactory)
|
|
||||||
|
|
||||||
`when`(userManager.canAddMoreUsers(eq(UserManager.USER_TYPE_FULL_SECONDARY)))
|
|
||||||
.thenReturn(true)
|
|
||||||
`when`(notificationShadeWindowView.context).thenReturn(context)
|
|
||||||
|
|
||||||
// Since userSwitcherController involves InteractionJankMonitor.
|
|
||||||
// Let's fulfill the dependencies.
|
|
||||||
val mockedContext = mock(Context::class.java)
|
|
||||||
doReturn(mockedContext).`when`(notificationShadeWindowView).context
|
|
||||||
doReturn(true).`when`(notificationShadeWindowView).isAttachedToWindow
|
|
||||||
doNothing().`when`(threadedRenderer).addObserver(any())
|
|
||||||
doNothing().`when`(threadedRenderer).removeObserver(any())
|
|
||||||
doReturn(threadedRenderer).`when`(notificationShadeWindowView).threadedRenderer
|
|
||||||
|
|
||||||
picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
|
|
||||||
|
|
||||||
// Create defaults for the current user
|
|
||||||
`when`(userTracker.userId).thenReturn(ownerId)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
|
||||||
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.ADD_USERS_WHEN_LOCKED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(0)
|
|
||||||
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.USER_SWITCHER_ENABLED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(1)
|
|
||||||
|
|
||||||
setupController()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupController() {
|
|
||||||
userSwitcherController =
|
|
||||||
UserSwitcherControllerOldImpl(
|
|
||||||
mContext,
|
|
||||||
activityManager,
|
|
||||||
userManager,
|
|
||||||
userTracker,
|
|
||||||
keyguardStateController,
|
|
||||||
deviceProvisionedController,
|
|
||||||
devicePolicyManager,
|
|
||||||
handler,
|
|
||||||
activityStarter,
|
|
||||||
broadcastDispatcher,
|
|
||||||
broadcastSender,
|
|
||||||
uiEventLogger,
|
|
||||||
falsingManager,
|
|
||||||
telephonyListenerManager,
|
|
||||||
secureSettings,
|
|
||||||
globalSettings,
|
|
||||||
bgExecutor,
|
|
||||||
longRunningExecutor,
|
|
||||||
uiExecutor,
|
|
||||||
interactionJankMonitor,
|
|
||||||
latencyTracker,
|
|
||||||
dumpManager,
|
|
||||||
dialogLaunchAnimator,
|
|
||||||
guestResumeSessionReceiver,
|
|
||||||
guestResetOrExitSessionReceiver
|
|
||||||
)
|
|
||||||
userSwitcherController.init(notificationShadeWindowView)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSwitchUser_parentDialogDismissed() {
|
|
||||||
val otherUserRecord = UserRecord(
|
|
||||||
secondaryUser,
|
|
||||||
picture,
|
|
||||||
false /* guest */,
|
|
||||||
false /* current */,
|
|
||||||
false /* isAddUser */,
|
|
||||||
false /* isRestricted */,
|
|
||||||
true /* isSwitchToEnabled */,
|
|
||||||
false /* isAddSupervisedUser */
|
|
||||||
)
|
|
||||||
`when`(userTracker.userId).thenReturn(ownerId)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(otherUserRecord, dialogShower)
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
|
|
||||||
verify(dialogShower).dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAddGuest_okButtonPressed() {
|
|
||||||
val emptyGuestUserRecord =
|
|
||||||
UserRecord(
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
true /* guest */,
|
|
||||||
false /* current */,
|
|
||||||
false /* isAddUser */,
|
|
||||||
false /* isRestricted */,
|
|
||||||
true /* isSwitchToEnabled */,
|
|
||||||
false /* isAddSupervisedUser */
|
|
||||||
)
|
|
||||||
`when`(userTracker.userId).thenReturn(ownerId)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
|
||||||
|
|
||||||
`when`(userManager.createGuest(any())).thenReturn(guestInfo)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, null)
|
|
||||||
bgExecutor.runAllReady()
|
|
||||||
uiExecutor.runAllReady()
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
verify(interactionJankMonitor).begin(any())
|
|
||||||
verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH)
|
|
||||||
verify(activityManager).switchUser(guestInfo.id)
|
|
||||||
assertEquals(1, uiEventLogger.numLogs())
|
|
||||||
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_ADD.id, uiEventLogger.eventId(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAddGuest_parentDialogDismissed() {
|
|
||||||
val emptyGuestUserRecord =
|
|
||||||
UserRecord(
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
true /* guest */,
|
|
||||||
false /* current */,
|
|
||||||
false /* isAddUser */,
|
|
||||||
false /* isRestricted */,
|
|
||||||
true /* isSwitchToEnabled */,
|
|
||||||
false /* isAddSupervisedUser */
|
|
||||||
)
|
|
||||||
`when`(userTracker.userId).thenReturn(ownerId)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
|
||||||
|
|
||||||
`when`(userManager.createGuest(any())).thenReturn(guestInfo)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, dialogShower)
|
|
||||||
bgExecutor.runAllReady()
|
|
||||||
uiExecutor.runAllReady()
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
verify(dialogShower).dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testRemoveGuest_removeButtonPressed_isLogged() {
|
|
||||||
val currentGuestUserRecord =
|
|
||||||
UserRecord(
|
|
||||||
guestInfo,
|
|
||||||
picture,
|
|
||||||
true /* guest */,
|
|
||||||
true /* current */,
|
|
||||||
false /* isAddUser */,
|
|
||||||
false /* isRestricted */,
|
|
||||||
true /* isSwitchToEnabled */,
|
|
||||||
false /* isAddSupervisedUser */
|
|
||||||
)
|
|
||||||
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
|
|
||||||
assertNotNull(userSwitcherController.mExitGuestDialog)
|
|
||||||
userSwitcherController.mExitGuestDialog
|
|
||||||
.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
assertEquals(1, uiEventLogger.numLogs())
|
|
||||||
assertTrue(
|
|
||||||
QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id == uiEventLogger.eventId(0) ||
|
|
||||||
QSUserSwitcherEvent.QS_USER_SWITCH.id == uiEventLogger.eventId(0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testRemoveGuest_removeButtonPressed_dialogDismissed() {
|
|
||||||
val currentGuestUserRecord =
|
|
||||||
UserRecord(
|
|
||||||
guestInfo,
|
|
||||||
picture,
|
|
||||||
true /* guest */,
|
|
||||||
true /* current */,
|
|
||||||
false /* isAddUser */,
|
|
||||||
false /* isRestricted */,
|
|
||||||
true /* isSwitchToEnabled */,
|
|
||||||
false /* isAddSupervisedUser */
|
|
||||||
)
|
|
||||||
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
|
|
||||||
assertNotNull(userSwitcherController.mExitGuestDialog)
|
|
||||||
userSwitcherController.mExitGuestDialog
|
|
||||||
.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
assertFalse(userSwitcherController.mExitGuestDialog.isShowing)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testRemoveGuest_dialogShowerUsed() {
|
|
||||||
val currentGuestUserRecord =
|
|
||||||
UserRecord(
|
|
||||||
guestInfo,
|
|
||||||
picture,
|
|
||||||
true /* guest */,
|
|
||||||
true /* current */,
|
|
||||||
false /* isAddUser */,
|
|
||||||
false /* isRestricted */,
|
|
||||||
true /* isSwitchToEnabled */,
|
|
||||||
false /* isAddSupervisedUser */
|
|
||||||
)
|
|
||||||
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, dialogShower)
|
|
||||||
assertNotNull(userSwitcherController.mExitGuestDialog)
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
verify(dialogShower)
|
|
||||||
.showDialog(
|
|
||||||
userSwitcherController.mExitGuestDialog,
|
|
||||||
DialogCuj(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN, "exit_guest_mode"))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testRemoveGuest_cancelButtonPressed_isNotLogged() {
|
|
||||||
val currentGuestUserRecord =
|
|
||||||
UserRecord(
|
|
||||||
guestInfo,
|
|
||||||
picture,
|
|
||||||
true /* guest */,
|
|
||||||
true /* current */,
|
|
||||||
false /* isAddUser */,
|
|
||||||
false /* isRestricted */,
|
|
||||||
true /* isSwitchToEnabled */,
|
|
||||||
false /* isAddSupervisedUser */
|
|
||||||
)
|
|
||||||
`when`(userTracker.userId).thenReturn(guestId)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
|
|
||||||
assertNotNull(userSwitcherController.mExitGuestDialog)
|
|
||||||
userSwitcherController.mExitGuestDialog
|
|
||||||
.getButton(DialogInterface.BUTTON_NEUTRAL).performClick()
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
assertEquals(0, uiEventLogger.numLogs())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testWipeGuest_startOverButtonPressed_isLogged() {
|
|
||||||
val currentGuestUserRecord =
|
|
||||||
UserRecord(
|
|
||||||
guestInfo,
|
|
||||||
picture,
|
|
||||||
true /* guest */,
|
|
||||||
false /* current */,
|
|
||||||
false /* isAddUser */,
|
|
||||||
false /* isRestricted */,
|
|
||||||
true /* isSwitchToEnabled */,
|
|
||||||
false /* isAddSupervisedUser */
|
|
||||||
)
|
|
||||||
`when`(userTracker.userId).thenReturn(guestId)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
|
||||||
|
|
||||||
// Simulate that guest user has already logged in
|
|
||||||
`when`(secureSettings.getIntForUser(
|
|
||||||
eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
|
|
||||||
.thenReturn(1)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
|
|
||||||
|
|
||||||
// Simulate a user switch event
|
|
||||||
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
|
|
||||||
|
|
||||||
assertNotNull(userSwitcherController.mGuestResumeSessionReceiver)
|
|
||||||
userSwitcherController.mGuestResumeSessionReceiver.onReceive(context, intent)
|
|
||||||
|
|
||||||
assertNotNull(userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog)
|
|
||||||
userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog
|
|
||||||
.getButton(GuestResumeSessionReceiver.ResetSessionDialog.BUTTON_WIPE).performClick()
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
assertEquals(1, uiEventLogger.numLogs())
|
|
||||||
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_WIPE.id, uiEventLogger.eventId(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testWipeGuest_continueButtonPressed_isLogged() {
|
|
||||||
val currentGuestUserRecord =
|
|
||||||
UserRecord(
|
|
||||||
guestInfo,
|
|
||||||
picture,
|
|
||||||
true /* guest */,
|
|
||||||
false /* current */,
|
|
||||||
false /* isAddUser */,
|
|
||||||
false /* isRestricted */,
|
|
||||||
true /* isSwitchToEnabled */,
|
|
||||||
false /* isAddSupervisedUser */
|
|
||||||
)
|
|
||||||
`when`(userTracker.userId).thenReturn(guestId)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
|
||||||
|
|
||||||
// Simulate that guest user has already logged in
|
|
||||||
`when`(secureSettings.getIntForUser(
|
|
||||||
eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
|
|
||||||
.thenReturn(1)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
|
|
||||||
|
|
||||||
// Simulate a user switch event
|
|
||||||
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
|
|
||||||
|
|
||||||
assertNotNull(userSwitcherController.mGuestResumeSessionReceiver)
|
|
||||||
userSwitcherController.mGuestResumeSessionReceiver.onReceive(context, intent)
|
|
||||||
|
|
||||||
assertNotNull(userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog)
|
|
||||||
userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog
|
|
||||||
.getButton(GuestResumeSessionReceiver.ResetSessionDialog.BUTTON_DONTWIPE)
|
|
||||||
.performClick()
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
assertEquals(1, uiEventLogger.numLogs())
|
|
||||||
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_CONTINUE.id, uiEventLogger.eventId(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun test_getCurrentUserName_shouldReturnNameOfTheCurrentUser() {
|
|
||||||
fun addUser(id: Int, name: String, isCurrent: Boolean) {
|
|
||||||
userSwitcherController.users.add(
|
|
||||||
UserRecord(
|
|
||||||
UserInfo(id, name, 0),
|
|
||||||
null, false, isCurrent, false,
|
|
||||||
false, false, false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val bgUserName = "background_user"
|
|
||||||
val fgUserName = "foreground_user"
|
|
||||||
|
|
||||||
addUser(1, bgUserName, false)
|
|
||||||
addUser(2, fgUserName, true)
|
|
||||||
|
|
||||||
assertEquals(fgUserName, userSwitcherController.currentUserName)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun isSystemUser_currentUserIsSystemUser_shouldReturnTrue() {
|
|
||||||
`when`(userTracker.userId).thenReturn(UserHandle.USER_SYSTEM)
|
|
||||||
assertEquals(true, userSwitcherController.isSystemUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun isSystemUser_currentUserIsNotSystemUser_shouldReturnFalse() {
|
|
||||||
`when`(userTracker.userId).thenReturn(1)
|
|
||||||
assertEquals(false, userSwitcherController.isSystemUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCanCreateSupervisedUserWithConfiguredPackage() {
|
|
||||||
// GIVEN the supervised user creation package is configured
|
|
||||||
`when`(context.getString(
|
|
||||||
com.android.internal.R.string.config_supervisedUserCreationPackage))
|
|
||||||
.thenReturn("some_pkg")
|
|
||||||
|
|
||||||
// AND the current user is allowed to create new users
|
|
||||||
`when`(userTracker.userId).thenReturn(ownerId)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
|
||||||
|
|
||||||
// WHEN the controller is started with the above config
|
|
||||||
setupController()
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
|
|
||||||
// THEN a supervised user can be constructed
|
|
||||||
assertTrue(userSwitcherController.canCreateSupervisedUser())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCannotCreateSupervisedUserWithConfiguredPackage() {
|
|
||||||
// GIVEN the supervised user creation package is NOT configured
|
|
||||||
`when`(context.getString(
|
|
||||||
com.android.internal.R.string.config_supervisedUserCreationPackage))
|
|
||||||
.thenReturn(null)
|
|
||||||
|
|
||||||
// AND the current user is allowed to create new users
|
|
||||||
`when`(userTracker.userId).thenReturn(ownerId)
|
|
||||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
|
||||||
|
|
||||||
// WHEN the controller is started with the above config
|
|
||||||
setupController()
|
|
||||||
testableLooper.processAllMessages()
|
|
||||||
|
|
||||||
// THEN a supervised user can NOT be constructed
|
|
||||||
assertFalse(userSwitcherController.canCreateSupervisedUser())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCannotCreateUserWhenUserSwitcherDisabled() {
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.USER_SWITCHER_ENABLED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(0)
|
|
||||||
setupController()
|
|
||||||
assertFalse(userSwitcherController.canCreateUser())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCannotCreateGuestUserWhenUserSwitcherDisabled() {
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.USER_SWITCHER_ENABLED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(0)
|
|
||||||
setupController()
|
|
||||||
assertFalse(userSwitcherController.canCreateGuest(false))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCannotCreateSupervisedUserWhenUserSwitcherDisabled() {
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.USER_SWITCHER_ENABLED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(0)
|
|
||||||
setupController()
|
|
||||||
assertFalse(userSwitcherController.canCreateSupervisedUser())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCanManageUser_userSwitcherEnabled_addUserWhenLocked() {
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.USER_SWITCHER_ENABLED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(1)
|
|
||||||
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.ADD_USERS_WHEN_LOCKED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(1)
|
|
||||||
setupController()
|
|
||||||
assertTrue(userSwitcherController.canManageUsers())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCanManageUser_userSwitcherDisabled_addUserWhenLocked() {
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.USER_SWITCHER_ENABLED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(0)
|
|
||||||
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.ADD_USERS_WHEN_LOCKED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(1)
|
|
||||||
setupController()
|
|
||||||
assertFalse(userSwitcherController.canManageUsers())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCanManageUser_userSwitcherEnabled_isAdmin() {
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.USER_SWITCHER_ENABLED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(1)
|
|
||||||
|
|
||||||
setupController()
|
|
||||||
assertTrue(userSwitcherController.canManageUsers())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCanManageUser_userSwitcherDisabled_isAdmin() {
|
|
||||||
`when`(
|
|
||||||
globalSettings.getIntForUser(
|
|
||||||
eq(Settings.Global.USER_SWITCHER_ENABLED),
|
|
||||||
anyInt(),
|
|
||||||
eq(UserHandle.USER_SYSTEM)
|
|
||||||
)
|
|
||||||
).thenReturn(0)
|
|
||||||
|
|
||||||
setupController()
|
|
||||||
assertFalse(userSwitcherController.canManageUsers())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun addUserSwitchCallback() {
|
|
||||||
val broadcastReceiverCaptor = argumentCaptor<BroadcastReceiver>()
|
|
||||||
verify(broadcastDispatcher).registerReceiver(
|
|
||||||
capture(broadcastReceiverCaptor),
|
|
||||||
any(),
|
|
||||||
nullable(), nullable(), anyInt(), nullable())
|
|
||||||
|
|
||||||
val cb = mock(UserSwitcherController.UserSwitchCallback::class.java)
|
|
||||||
userSwitcherController.addUserSwitchCallback(cb)
|
|
||||||
|
|
||||||
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
|
|
||||||
broadcastReceiverCaptor.value.onReceive(context, intent)
|
|
||||||
verify(cb).onUserSwitched()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onUserItemClicked_guest_runsOnBgThread() {
|
|
||||||
val dialogShower = mock(UserSwitchDialogController.DialogShower::class.java)
|
|
||||||
val guestUserRecord = UserRecord(
|
|
||||||
null,
|
|
||||||
picture,
|
|
||||||
true /* guest */,
|
|
||||||
false /* current */,
|
|
||||||
false /* isAddUser */,
|
|
||||||
false /* isRestricted */,
|
|
||||||
true /* isSwitchToEnabled */,
|
|
||||||
false /* isAddSupervisedUser */
|
|
||||||
)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(guestUserRecord, dialogShower)
|
|
||||||
assertTrue(bgExecutor.numPending() > 0)
|
|
||||||
verify(userManager, never()).createGuest(context)
|
|
||||||
bgExecutor.runAllReady()
|
|
||||||
verify(userManager).createGuest(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun onUserItemClicked_manageUsers() {
|
|
||||||
val manageUserRecord = LegacyUserDataHelper.createRecord(
|
|
||||||
mContext,
|
|
||||||
ownerId,
|
|
||||||
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
|
||||||
isRestricted = false,
|
|
||||||
isSwitchToEnabled = true
|
|
||||||
)
|
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(manageUserRecord, null)
|
|
||||||
val intentCaptor = kotlinArgumentCaptor<Intent>()
|
|
||||||
verify(activityStarter).startActivity(intentCaptor.capture(),
|
|
||||||
eq(true)
|
|
||||||
)
|
|
||||||
Truth.assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_USER_SETTINGS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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,
|
|
||||||
isLastGuestUser = true,
|
|
||||||
selectedIndex = 1,
|
|
||||||
)
|
|
||||||
underTest.refreshUsers()
|
|
||||||
assertThat(userInfos).isEqualTo(thirdExpectedValue)
|
|
||||||
assertThat(selectedUserInfo).isEqualTo(thirdExpectedValue[1])
|
|
||||||
assertThat(selectedUserInfo?.isGuest).isTrue()
|
|
||||||
assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedNonGuestUserId)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `refreshUsers - sorts by creation time - guest user last`() = runSelfCancelingTest {
|
|
||||||
underTest = create(this)
|
|
||||||
val unsortedUsers =
|
|
||||||
setUpUsers(
|
|
||||||
count = 3,
|
|
||||||
selectedIndex = 0,
|
|
||||||
isLastGuestUser = true,
|
|
||||||
)
|
|
||||||
unsortedUsers[0].creationTime = 999
|
|
||||||
unsortedUsers[1].creationTime = 900
|
|
||||||
unsortedUsers[2].creationTime = 950
|
|
||||||
val expectedUsers =
|
|
||||||
listOf(
|
|
||||||
unsortedUsers[1],
|
|
||||||
unsortedUsers[0],
|
|
||||||
unsortedUsers[2], // last because this is the guest
|
|
||||||
)
|
|
||||||
var userInfos: List<UserInfo>? = null
|
|
||||||
underTest.userInfos.onEach { userInfos = it }.launchIn(this)
|
|
||||||
|
|
||||||
underTest.refreshUsers()
|
|
||||||
assertThat(userInfos).isEqualTo(expectedUsers)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `userTrackerCallback - updates selectedUserInfo`() = runSelfCancelingTest {
|
|
||||||
underTest = create(this)
|
|
||||||
var selectedUserInfo: UserInfo? = null
|
|
||||||
underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
|
|
||||||
setUpUsers(
|
|
||||||
count = 2,
|
|
||||||
selectedIndex = 0,
|
|
||||||
)
|
|
||||||
tracker.onProfileChanged()
|
|
||||||
assertThat(selectedUserInfo?.id == 0)
|
|
||||||
setUpUsers(
|
|
||||||
count = 2,
|
|
||||||
selectedIndex = 1,
|
|
||||||
)
|
|
||||||
tracker.onProfileChanged()
|
|
||||||
assertThat(selectedUserInfo?.id == 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpUsers(
|
|
||||||
count: Int,
|
|
||||||
isLastGuestUser: Boolean = false,
|
|
||||||
selectedIndex: Int = 0,
|
|
||||||
): List<UserInfo> {
|
|
||||||
val userInfos =
|
|
||||||
(0 until count).map { index ->
|
|
||||||
createUserInfo(
|
|
||||||
index,
|
|
||||||
isGuest = isLastGuestUser && 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,54 +17,263 @@
|
|||||||
|
|
||||||
package com.android.systemui.user.data.repository
|
package com.android.systemui.user.data.repository
|
||||||
|
|
||||||
|
import android.content.pm.UserInfo
|
||||||
|
import android.os.UserHandle
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
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.settings.FakeUserTracker
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
||||||
import com.android.systemui.util.settings.FakeSettings
|
import com.android.systemui.util.settings.FakeSettings
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
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 kotlinx.coroutines.test.TestCoroutineScope
|
import kotlinx.coroutines.test.TestCoroutineScope
|
||||||
|
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.Mock
|
||||||
|
import org.mockito.Mockito.`when` as whenever
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
abstract class UserRepositoryImplTest : SysuiTestCase() {
|
@SmallTest
|
||||||
|
@RunWith(JUnit4::class)
|
||||||
|
class UserRepositoryImplTest : SysuiTestCase() {
|
||||||
|
|
||||||
@Mock protected lateinit var manager: UserManager
|
@Mock private lateinit var manager: UserManager
|
||||||
@Mock protected lateinit var controller: UserSwitcherController
|
|
||||||
|
|
||||||
protected lateinit var underTest: UserRepositoryImpl
|
private lateinit var underTest: UserRepositoryImpl
|
||||||
|
|
||||||
protected lateinit var globalSettings: FakeSettings
|
private lateinit var globalSettings: FakeSettings
|
||||||
protected lateinit var tracker: FakeUserTracker
|
private lateinit var tracker: FakeUserTracker
|
||||||
protected lateinit var featureFlags: FakeFeatureFlags
|
|
||||||
|
|
||||||
protected fun setUp(isRefactored: Boolean) {
|
@Before
|
||||||
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
globalSettings = FakeSettings()
|
globalSettings = FakeSettings()
|
||||||
tracker = FakeUserTracker()
|
tracker = FakeUserTracker()
|
||||||
featureFlags = FakeFeatureFlags()
|
|
||||||
featureFlags.set(Flags.USER_INTERACTOR_AND_REPO_USE_CONTROLLER, !isRefactored)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun create(scope: CoroutineScope = TestCoroutineScope()): UserRepositoryImpl {
|
@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,
|
||||||
|
isLastGuestUser = true,
|
||||||
|
selectedIndex = 1,
|
||||||
|
)
|
||||||
|
underTest.refreshUsers()
|
||||||
|
assertThat(userInfos).isEqualTo(thirdExpectedValue)
|
||||||
|
assertThat(selectedUserInfo).isEqualTo(thirdExpectedValue[1])
|
||||||
|
assertThat(selectedUserInfo?.isGuest).isTrue()
|
||||||
|
assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedNonGuestUserId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `refreshUsers - sorts by creation time - guest user last`() = runSelfCancelingTest {
|
||||||
|
underTest = create(this)
|
||||||
|
val unsortedUsers =
|
||||||
|
setUpUsers(
|
||||||
|
count = 3,
|
||||||
|
selectedIndex = 0,
|
||||||
|
isLastGuestUser = true,
|
||||||
|
)
|
||||||
|
unsortedUsers[0].creationTime = 999
|
||||||
|
unsortedUsers[1].creationTime = 900
|
||||||
|
unsortedUsers[2].creationTime = 950
|
||||||
|
val expectedUsers =
|
||||||
|
listOf(
|
||||||
|
unsortedUsers[1],
|
||||||
|
unsortedUsers[0],
|
||||||
|
unsortedUsers[2], // last because this is the guest
|
||||||
|
)
|
||||||
|
var userInfos: List<UserInfo>? = null
|
||||||
|
underTest.userInfos.onEach { userInfos = it }.launchIn(this)
|
||||||
|
|
||||||
|
underTest.refreshUsers()
|
||||||
|
assertThat(userInfos).isEqualTo(expectedUsers)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpUsers(
|
||||||
|
count: Int,
|
||||||
|
isLastGuestUser: Boolean = false,
|
||||||
|
selectedIndex: Int = 0,
|
||||||
|
): List<UserInfo> {
|
||||||
|
val userInfos =
|
||||||
|
(0 until count).map { index ->
|
||||||
|
createUserInfo(
|
||||||
|
index,
|
||||||
|
isGuest = isLastGuestUser && index == count - 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
whenever(manager.aliveUsers).thenReturn(userInfos)
|
||||||
|
tracker.set(userInfos, selectedIndex)
|
||||||
|
return userInfos
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun `userTrackerCallback - updates selectedUserInfo`() = runSelfCancelingTest {
|
||||||
|
underTest = create(this)
|
||||||
|
var selectedUserInfo: UserInfo? = null
|
||||||
|
underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
|
||||||
|
setUpUsers(
|
||||||
|
count = 2,
|
||||||
|
selectedIndex = 0,
|
||||||
|
)
|
||||||
|
tracker.onProfileChanged()
|
||||||
|
assertThat(selectedUserInfo?.id).isEqualTo(0)
|
||||||
|
setUpUsers(
|
||||||
|
count = 2,
|
||||||
|
selectedIndex = 1,
|
||||||
|
)
|
||||||
|
tracker.onProfileChanged()
|
||||||
|
assertThat(selectedUserInfo?.id).isEqualTo(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun create(scope: CoroutineScope = TestCoroutineScope()): UserRepositoryImpl {
|
||||||
return UserRepositoryImpl(
|
return UserRepositoryImpl(
|
||||||
appContext = context,
|
appContext = context,
|
||||||
manager = manager,
|
manager = manager,
|
||||||
controller = controller,
|
|
||||||
applicationScope = scope,
|
applicationScope = scope,
|
||||||
mainDispatcher = IMMEDIATE,
|
mainDispatcher = IMMEDIATE,
|
||||||
backgroundDispatcher = IMMEDIATE,
|
backgroundDispatcher = IMMEDIATE,
|
||||||
globalSettings = globalSettings,
|
globalSettings = globalSettings,
|
||||||
tracker = tracker,
|
tracker = tracker,
|
||||||
featureFlags = featureFlags,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic protected val IMMEDIATE = Dispatchers.Main.immediate
|
@JvmStatic private val IMMEDIATE = Dispatchers.Main.immediate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,209 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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),
|
|
||||||
createActionRecord(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
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),
|
|
||||||
createActionRecord(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
var models: List<UserActionModel>? = null
|
|
||||||
val job = underTest.actions.onEach { models = it }.launchIn(this)
|
|
||||||
|
|
||||||
assertThat(models).hasSize(4)
|
|
||||||
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)
|
|
||||||
assertThat(models?.get(3)).isEqualTo(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
|
|
||||||
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,
|
|
||||||
isManageUsers = action == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,740 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.content.pm.UserInfo
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.os.UserHandle
|
|
||||||
import android.os.UserManager
|
|
||||||
import android.provider.Settings
|
|
||||||
import androidx.test.filters.SmallTest
|
|
||||||
import com.android.internal.R.drawable.ic_account_circle
|
|
||||||
import com.android.systemui.R
|
|
||||||
import com.android.systemui.common.shared.model.Text
|
|
||||||
import com.android.systemui.qs.user.UserSwitchDialogController
|
|
||||||
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
|
||||||
import com.android.systemui.user.data.source.UserRecord
|
|
||||||
import com.android.systemui.user.domain.model.ShowDialogRequestModel
|
|
||||||
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.eq
|
|
||||||
import com.android.systemui.util.mockito.kotlinArgumentCaptor
|
|
||||||
import com.android.systemui.util.mockito.mock
|
|
||||||
import com.android.systemui.util.mockito.whenever
|
|
||||||
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 kotlinx.coroutines.test.advanceUntilIdle
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.junit.runners.JUnit4
|
|
||||||
import org.mockito.ArgumentMatchers.anyBoolean
|
|
||||||
import org.mockito.ArgumentMatchers.anyInt
|
|
||||||
import org.mockito.Mockito.never
|
|
||||||
import org.mockito.Mockito.verify
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
@RunWith(JUnit4::class)
|
|
||||||
class UserInteractorRefactoredTest : UserInteractorTest() {
|
|
||||||
|
|
||||||
override fun isRefactored(): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
override fun setUp() {
|
|
||||||
super.setUp()
|
|
||||||
|
|
||||||
overrideResource(R.drawable.ic_account_circle, GUEST_ICON)
|
|
||||||
overrideResource(R.dimen.max_avatar_size, 10)
|
|
||||||
overrideResource(
|
|
||||||
com.android.internal.R.string.config_supervisedUserCreationPackage,
|
|
||||||
SUPERVISED_USER_CREATION_APP_PACKAGE,
|
|
||||||
)
|
|
||||||
whenever(manager.getUserIcon(anyInt())).thenReturn(ICON)
|
|
||||||
whenever(manager.canAddMoreUsers(any())).thenReturn(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `onRecordSelected - user`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 3, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
|
|
||||||
underTest.onRecordSelected(UserRecord(info = userInfos[1]), dialogShower)
|
|
||||||
|
|
||||||
verify(dialogShower).dismiss()
|
|
||||||
verify(activityManager).switchUser(userInfos[1].id)
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `onRecordSelected - switch to guest user`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 3, includeGuest = true)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
|
|
||||||
underTest.onRecordSelected(UserRecord(info = userInfos.last()))
|
|
||||||
|
|
||||||
verify(activityManager).switchUser(userInfos.last().id)
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `onRecordSelected - enter guest mode`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 3, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
val guestUserInfo = createUserInfo(id = 1337, name = "guest", isGuest = true)
|
|
||||||
whenever(manager.createGuest(any())).thenReturn(guestUserInfo)
|
|
||||||
|
|
||||||
underTest.onRecordSelected(UserRecord(isGuest = true), dialogShower)
|
|
||||||
|
|
||||||
verify(dialogShower).dismiss()
|
|
||||||
verify(manager).createGuest(any())
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `onRecordSelected - action`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 3, includeGuest = true)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
|
|
||||||
underTest.onRecordSelected(UserRecord(isAddSupervisedUser = true), dialogShower)
|
|
||||||
|
|
||||||
verify(dialogShower, never()).dismiss()
|
|
||||||
verify(activityStarter).startActivity(any(), anyBoolean())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `users - switcher enabled`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 3, includeGuest = true)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
|
|
||||||
var value: List<UserModel>? = null
|
|
||||||
val job = underTest.users.onEach { value = it }.launchIn(this)
|
|
||||||
assertUsers(models = value, count = 3, includeGuest = true)
|
|
||||||
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `users - switches to second user`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
|
|
||||||
var value: List<UserModel>? = null
|
|
||||||
val job = underTest.users.onEach { value = it }.launchIn(this)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[1])
|
|
||||||
|
|
||||||
assertUsers(models = value, count = 2, selectedIndex = 1)
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `users - switcher not enabled`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = false))
|
|
||||||
|
|
||||||
var value: List<UserModel>? = null
|
|
||||||
val job = underTest.users.onEach { value = it }.launchIn(this)
|
|
||||||
assertUsers(models = value, count = 1)
|
|
||||||
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun selectedUser() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
|
|
||||||
var value: UserModel? = null
|
|
||||||
val job = underTest.selectedUser.onEach { value = it }.launchIn(this)
|
|
||||||
assertUser(value, id = 0, isSelected = true)
|
|
||||||
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[1])
|
|
||||||
assertUser(value, id = 1, isSelected = true)
|
|
||||||
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `actions - device unlocked`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
keyguardRepository.setKeyguardShowing(false)
|
|
||||||
var value: List<UserActionModel>? = null
|
|
||||||
val job = underTest.actions.onEach { value = it }.launchIn(this)
|
|
||||||
|
|
||||||
assertThat(value)
|
|
||||||
.isEqualTo(
|
|
||||||
listOf(
|
|
||||||
UserActionModel.ENTER_GUEST_MODE,
|
|
||||||
UserActionModel.ADD_USER,
|
|
||||||
UserActionModel.ADD_SUPERVISED_USER,
|
|
||||||
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `actions - device unlocked user not primary - empty list`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[1])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
keyguardRepository.setKeyguardShowing(false)
|
|
||||||
var value: List<UserActionModel>? = null
|
|
||||||
val job = underTest.actions.onEach { value = it }.launchIn(this)
|
|
||||||
|
|
||||||
assertThat(value).isEqualTo(emptyList<UserActionModel>())
|
|
||||||
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `actions - device unlocked user is guest - empty list`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = true)
|
|
||||||
assertThat(userInfos[1].isGuest).isTrue()
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[1])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
keyguardRepository.setKeyguardShowing(false)
|
|
||||||
var value: List<UserActionModel>? = null
|
|
||||||
val job = underTest.actions.onEach { value = it }.launchIn(this)
|
|
||||||
|
|
||||||
assertThat(value).isEqualTo(emptyList<UserActionModel>())
|
|
||||||
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `actions - device locked add from lockscreen set - full list`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(
|
|
||||||
UserSwitcherSettingsModel(
|
|
||||||
isUserSwitcherEnabled = true,
|
|
||||||
isAddUsersFromLockscreen = true,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
keyguardRepository.setKeyguardShowing(false)
|
|
||||||
var value: List<UserActionModel>? = null
|
|
||||||
val job = underTest.actions.onEach { value = it }.launchIn(this)
|
|
||||||
|
|
||||||
assertThat(value)
|
|
||||||
.isEqualTo(
|
|
||||||
listOf(
|
|
||||||
UserActionModel.ENTER_GUEST_MODE,
|
|
||||||
UserActionModel.ADD_USER,
|
|
||||||
UserActionModel.ADD_SUPERVISED_USER,
|
|
||||||
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `actions - device locked - only guest action and manage user is shown`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
keyguardRepository.setKeyguardShowing(true)
|
|
||||||
var value: List<UserActionModel>? = null
|
|
||||||
val job = underTest.actions.onEach { value = it }.launchIn(this)
|
|
||||||
|
|
||||||
assertThat(value)
|
|
||||||
.isEqualTo(
|
|
||||||
listOf(
|
|
||||||
UserActionModel.ENTER_GUEST_MODE,
|
|
||||||
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `executeAction - add user - dialog shown`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
keyguardRepository.setKeyguardShowing(false)
|
|
||||||
var dialogRequest: ShowDialogRequestModel? = null
|
|
||||||
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
|
|
||||||
val dialogShower: UserSwitchDialogController.DialogShower = mock()
|
|
||||||
|
|
||||||
underTest.executeAction(UserActionModel.ADD_USER, dialogShower)
|
|
||||||
assertThat(dialogRequest)
|
|
||||||
.isEqualTo(
|
|
||||||
ShowDialogRequestModel.ShowAddUserDialog(
|
|
||||||
userHandle = userInfos[0].userHandle,
|
|
||||||
isKeyguardShowing = false,
|
|
||||||
showEphemeralMessage = false,
|
|
||||||
dialogShower = dialogShower,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
underTest.onDialogShown()
|
|
||||||
assertThat(dialogRequest).isNull()
|
|
||||||
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `executeAction - add supervised user - starts activity`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)
|
|
||||||
|
|
||||||
val intentCaptor = kotlinArgumentCaptor<Intent>()
|
|
||||||
verify(activityStarter).startActivity(intentCaptor.capture(), eq(true))
|
|
||||||
assertThat(intentCaptor.value.action)
|
|
||||||
.isEqualTo(UserManager.ACTION_CREATE_SUPERVISED_USER)
|
|
||||||
assertThat(intentCaptor.value.`package`).isEqualTo(SUPERVISED_USER_CREATION_APP_PACKAGE)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `executeAction - navigate to manage users`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
|
|
||||||
|
|
||||||
val intentCaptor = kotlinArgumentCaptor<Intent>()
|
|
||||||
verify(activityStarter).startActivity(intentCaptor.capture(), eq(true))
|
|
||||||
assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_USER_SETTINGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `executeAction - guest mode`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
val guestUserInfo = createUserInfo(id = 1337, name = "guest", isGuest = true)
|
|
||||||
whenever(manager.createGuest(any())).thenReturn(guestUserInfo)
|
|
||||||
val dialogRequests = mutableListOf<ShowDialogRequestModel?>()
|
|
||||||
val showDialogsJob =
|
|
||||||
underTest.dialogShowRequests
|
|
||||||
.onEach {
|
|
||||||
dialogRequests.add(it)
|
|
||||||
if (it != null) {
|
|
||||||
underTest.onDialogShown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.launchIn(this)
|
|
||||||
val dismissDialogsJob =
|
|
||||||
underTest.dialogDismissRequests
|
|
||||||
.onEach {
|
|
||||||
if (it != null) {
|
|
||||||
underTest.onDialogDismissed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.launchIn(this)
|
|
||||||
|
|
||||||
underTest.executeAction(UserActionModel.ENTER_GUEST_MODE)
|
|
||||||
|
|
||||||
assertThat(dialogRequests)
|
|
||||||
.contains(
|
|
||||||
ShowDialogRequestModel.ShowUserCreationDialog(isGuest = true),
|
|
||||||
)
|
|
||||||
verify(activityManager).switchUser(guestUserInfo.id)
|
|
||||||
|
|
||||||
showDialogsJob.cancel()
|
|
||||||
dismissDialogsJob.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `selectUser - already selected guest re-selected - exit guest dialog`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = true)
|
|
||||||
val guestUserInfo = userInfos[1]
|
|
||||||
assertThat(guestUserInfo.isGuest).isTrue()
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(guestUserInfo)
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
var dialogRequest: ShowDialogRequestModel? = null
|
|
||||||
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
|
|
||||||
|
|
||||||
underTest.selectUser(
|
|
||||||
newlySelectedUserId = guestUserInfo.id,
|
|
||||||
dialogShower = dialogShower,
|
|
||||||
)
|
|
||||||
|
|
||||||
assertThat(dialogRequest)
|
|
||||||
.isInstanceOf(ShowDialogRequestModel.ShowExitGuestDialog::class.java)
|
|
||||||
verify(dialogShower, never()).dismiss()
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `selectUser - currently guest non-guest selected - exit guest dialog`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = true)
|
|
||||||
val guestUserInfo = userInfos[1]
|
|
||||||
assertThat(guestUserInfo.isGuest).isTrue()
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(guestUserInfo)
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
var dialogRequest: ShowDialogRequestModel? = null
|
|
||||||
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
|
|
||||||
|
|
||||||
underTest.selectUser(newlySelectedUserId = userInfos[0].id, dialogShower = dialogShower)
|
|
||||||
|
|
||||||
assertThat(dialogRequest)
|
|
||||||
.isInstanceOf(ShowDialogRequestModel.ShowExitGuestDialog::class.java)
|
|
||||||
verify(dialogShower, never()).dismiss()
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `selectUser - not currently guest - switches users`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
var dialogRequest: ShowDialogRequestModel? = null
|
|
||||||
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
|
|
||||||
|
|
||||||
underTest.selectUser(newlySelectedUserId = userInfos[1].id, dialogShower = dialogShower)
|
|
||||||
|
|
||||||
assertThat(dialogRequest).isNull()
|
|
||||||
verify(activityManager).switchUser(userInfos[1].id)
|
|
||||||
verify(dialogShower).dismiss()
|
|
||||||
job.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `Telephony call state changes - refreshes users`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val refreshUsersCallCount = userRepository.refreshUsersCallCount
|
|
||||||
|
|
||||||
telephonyRepository.setCallState(1)
|
|
||||||
|
|
||||||
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `User switched broadcast`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
val callback1: UserInteractor.UserCallback = mock()
|
|
||||||
val callback2: UserInteractor.UserCallback = mock()
|
|
||||||
underTest.addCallback(callback1)
|
|
||||||
underTest.addCallback(callback2)
|
|
||||||
val refreshUsersCallCount = userRepository.refreshUsersCallCount
|
|
||||||
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[1])
|
|
||||||
fakeBroadcastDispatcher.registeredReceivers.forEach {
|
|
||||||
it.onReceive(
|
|
||||||
context,
|
|
||||||
Intent(Intent.ACTION_USER_SWITCHED)
|
|
||||||
.putExtra(Intent.EXTRA_USER_HANDLE, userInfos[1].id),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
verify(callback1).onUserStateChanged()
|
|
||||||
verify(callback2).onUserStateChanged()
|
|
||||||
assertThat(userRepository.secondaryUserId).isEqualTo(userInfos[1].id)
|
|
||||||
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `User info changed broadcast`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
val refreshUsersCallCount = userRepository.refreshUsersCallCount
|
|
||||||
|
|
||||||
fakeBroadcastDispatcher.registeredReceivers.forEach {
|
|
||||||
it.onReceive(
|
|
||||||
context,
|
|
||||||
Intent(Intent.ACTION_USER_INFO_CHANGED),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `System user unlocked broadcast - refresh users`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
val refreshUsersCallCount = userRepository.refreshUsersCallCount
|
|
||||||
|
|
||||||
fakeBroadcastDispatcher.registeredReceivers.forEach {
|
|
||||||
it.onReceive(
|
|
||||||
context,
|
|
||||||
Intent(Intent.ACTION_USER_UNLOCKED)
|
|
||||||
.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `Non-system user unlocked broadcast - do not refresh users`() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
val refreshUsersCallCount = userRepository.refreshUsersCallCount
|
|
||||||
|
|
||||||
fakeBroadcastDispatcher.registeredReceivers.forEach {
|
|
||||||
it.onReceive(
|
|
||||||
context,
|
|
||||||
Intent(Intent.ACTION_USER_UNLOCKED).putExtra(Intent.EXTRA_USER_HANDLE, 1337),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun userRecords() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 3, includeGuest = false)
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
keyguardRepository.setKeyguardShowing(false)
|
|
||||||
|
|
||||||
testCoroutineScope.advanceUntilIdle()
|
|
||||||
|
|
||||||
assertRecords(
|
|
||||||
records = underTest.userRecords.value,
|
|
||||||
userIds = listOf(0, 1, 2),
|
|
||||||
selectedUserIndex = 0,
|
|
||||||
includeGuest = false,
|
|
||||||
expectedActions =
|
|
||||||
listOf(
|
|
||||||
UserActionModel.ENTER_GUEST_MODE,
|
|
||||||
UserActionModel.ADD_USER,
|
|
||||||
UserActionModel.ADD_SUPERVISED_USER,
|
|
||||||
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun selectedUserRecord() =
|
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
val userInfos = createUserInfos(count = 3, includeGuest = true)
|
|
||||||
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
|
||||||
userRepository.setUserInfos(userInfos)
|
|
||||||
userRepository.setSelectedUserInfo(userInfos[0])
|
|
||||||
keyguardRepository.setKeyguardShowing(false)
|
|
||||||
|
|
||||||
assertRecordForUser(
|
|
||||||
record = underTest.selectedUserRecord.value,
|
|
||||||
id = 0,
|
|
||||||
hasPicture = true,
|
|
||||||
isCurrent = true,
|
|
||||||
isSwitchToEnabled = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assertUsers(
|
|
||||||
models: List<UserModel>?,
|
|
||||||
count: Int,
|
|
||||||
selectedIndex: Int = 0,
|
|
||||||
includeGuest: Boolean = false,
|
|
||||||
) {
|
|
||||||
checkNotNull(models)
|
|
||||||
assertThat(models.size).isEqualTo(count)
|
|
||||||
models.forEachIndexed { index, model ->
|
|
||||||
assertUser(
|
|
||||||
model = model,
|
|
||||||
id = index,
|
|
||||||
isSelected = index == selectedIndex,
|
|
||||||
isGuest = includeGuest && index == count - 1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assertUser(
|
|
||||||
model: UserModel?,
|
|
||||||
id: Int,
|
|
||||||
isSelected: Boolean = false,
|
|
||||||
isGuest: Boolean = false,
|
|
||||||
) {
|
|
||||||
checkNotNull(model)
|
|
||||||
assertThat(model.id).isEqualTo(id)
|
|
||||||
assertThat(model.name).isEqualTo(Text.Loaded(if (isGuest) "guest" else "user_$id"))
|
|
||||||
assertThat(model.isSelected).isEqualTo(isSelected)
|
|
||||||
assertThat(model.isSelectable).isTrue()
|
|
||||||
assertThat(model.isGuest).isEqualTo(isGuest)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assertRecords(
|
|
||||||
records: List<UserRecord>,
|
|
||||||
userIds: List<Int>,
|
|
||||||
selectedUserIndex: Int = 0,
|
|
||||||
includeGuest: Boolean = false,
|
|
||||||
expectedActions: List<UserActionModel> = emptyList(),
|
|
||||||
) {
|
|
||||||
assertThat(records.size >= userIds.size).isTrue()
|
|
||||||
userIds.indices.forEach { userIndex ->
|
|
||||||
val record = records[userIndex]
|
|
||||||
assertThat(record.info).isNotNull()
|
|
||||||
val isGuest = includeGuest && userIndex == userIds.size - 1
|
|
||||||
assertRecordForUser(
|
|
||||||
record = record,
|
|
||||||
id = userIds[userIndex],
|
|
||||||
hasPicture = !isGuest,
|
|
||||||
isCurrent = userIndex == selectedUserIndex,
|
|
||||||
isGuest = isGuest,
|
|
||||||
isSwitchToEnabled = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(records.size - userIds.size).isEqualTo(expectedActions.size)
|
|
||||||
(userIds.size until userIds.size + expectedActions.size).forEach { actionIndex ->
|
|
||||||
val record = records[actionIndex]
|
|
||||||
assertThat(record.info).isNull()
|
|
||||||
assertRecordForAction(
|
|
||||||
record = record,
|
|
||||||
type = expectedActions[actionIndex - userIds.size],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assertRecordForUser(
|
|
||||||
record: UserRecord?,
|
|
||||||
id: Int? = null,
|
|
||||||
hasPicture: Boolean = false,
|
|
||||||
isCurrent: Boolean = false,
|
|
||||||
isGuest: Boolean = false,
|
|
||||||
isSwitchToEnabled: Boolean = false,
|
|
||||||
) {
|
|
||||||
checkNotNull(record)
|
|
||||||
assertThat(record.info?.id).isEqualTo(id)
|
|
||||||
assertThat(record.picture != null).isEqualTo(hasPicture)
|
|
||||||
assertThat(record.isCurrent).isEqualTo(isCurrent)
|
|
||||||
assertThat(record.isGuest).isEqualTo(isGuest)
|
|
||||||
assertThat(record.isSwitchToEnabled).isEqualTo(isSwitchToEnabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assertRecordForAction(
|
|
||||||
record: UserRecord,
|
|
||||||
type: UserActionModel,
|
|
||||||
) {
|
|
||||||
assertThat(record.isGuest).isEqualTo(type == UserActionModel.ENTER_GUEST_MODE)
|
|
||||||
assertThat(record.isAddUser).isEqualTo(type == UserActionModel.ADD_USER)
|
|
||||||
assertThat(record.isAddSupervisedUser)
|
|
||||||
.isEqualTo(type == UserActionModel.ADD_SUPERVISED_USER)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createUserInfos(
|
|
||||||
count: Int,
|
|
||||||
includeGuest: Boolean,
|
|
||||||
): List<UserInfo> {
|
|
||||||
return (0 until count).map { index ->
|
|
||||||
val isGuest = includeGuest && index == count - 1
|
|
||||||
createUserInfo(
|
|
||||||
id = index,
|
|
||||||
name =
|
|
||||||
if (isGuest) {
|
|
||||||
"guest"
|
|
||||||
} else {
|
|
||||||
"user_$index"
|
|
||||||
},
|
|
||||||
isPrimary = !isGuest && index == 0,
|
|
||||||
isGuest = isGuest,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createUserInfo(
|
|
||||||
id: Int,
|
|
||||||
name: String,
|
|
||||||
isPrimary: Boolean = false,
|
|
||||||
isGuest: Boolean = false,
|
|
||||||
): UserInfo {
|
|
||||||
return UserInfo(
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
/* iconPath= */ "",
|
|
||||||
/* flags= */ if (isPrimary) {
|
|
||||||
UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
},
|
|
||||||
if (isGuest) {
|
|
||||||
UserManager.USER_TYPE_FULL_GUEST
|
|
||||||
} else {
|
|
||||||
UserManager.USER_TYPE_FULL_SYSTEM
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
|
||||||
private val ICON = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
|
|
||||||
private val GUEST_ICON: Drawable = mock()
|
|
||||||
private const val SUPERVISED_USER_CREATION_APP_PACKAGE = "supervisedUserCreation"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,51 +19,90 @@ package com.android.systemui.user.domain.interactor
|
|||||||
|
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.app.admin.DevicePolicyManager
|
import android.app.admin.DevicePolicyManager
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.UserInfo
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.os.UserHandle
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.internal.R.drawable.ic_account_circle
|
||||||
import com.android.internal.logging.UiEventLogger
|
import com.android.internal.logging.UiEventLogger
|
||||||
import com.android.systemui.GuestResetOrExitSessionReceiver
|
import com.android.systemui.GuestResetOrExitSessionReceiver
|
||||||
import com.android.systemui.GuestResumeSessionReceiver
|
import com.android.systemui.GuestResumeSessionReceiver
|
||||||
|
import com.android.systemui.R
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.flags.FakeFeatureFlags
|
import com.android.systemui.common.shared.model.Text
|
||||||
import com.android.systemui.flags.Flags
|
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.qs.user.UserSwitchDialogController
|
import com.android.systemui.qs.user.UserSwitchDialogController
|
||||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController
|
import com.android.systemui.statusbar.policy.DeviceProvisionedController
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
|
||||||
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
|
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
|
||||||
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
|
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
|
||||||
|
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
||||||
import com.android.systemui.user.data.repository.FakeUserRepository
|
import com.android.systemui.user.data.repository.FakeUserRepository
|
||||||
|
import com.android.systemui.user.data.source.UserRecord
|
||||||
|
import com.android.systemui.user.domain.model.ShowDialogRequestModel
|
||||||
|
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.eq
|
||||||
|
import com.android.systemui.util.mockito.kotlinArgumentCaptor
|
||||||
|
import com.android.systemui.util.mockito.mock
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.test.TestCoroutineScope
|
import kotlinx.coroutines.test.TestCoroutineScope
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.JUnit4
|
||||||
|
import org.mockito.ArgumentMatchers.anyBoolean
|
||||||
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.never
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
abstract class UserInteractorTest : SysuiTestCase() {
|
@SmallTest
|
||||||
|
@RunWith(JUnit4::class)
|
||||||
|
class UserInteractorTest : SysuiTestCase() {
|
||||||
|
|
||||||
@Mock protected lateinit var controller: UserSwitcherController
|
@Mock private lateinit var activityStarter: ActivityStarter
|
||||||
@Mock protected lateinit var activityStarter: ActivityStarter
|
@Mock private lateinit var manager: UserManager
|
||||||
@Mock protected lateinit var manager: UserManager
|
@Mock private lateinit var activityManager: ActivityManager
|
||||||
@Mock protected lateinit var activityManager: ActivityManager
|
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
|
||||||
@Mock protected lateinit var deviceProvisionedController: DeviceProvisionedController
|
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
|
||||||
@Mock protected lateinit var devicePolicyManager: DevicePolicyManager
|
@Mock private lateinit var uiEventLogger: UiEventLogger
|
||||||
@Mock protected lateinit var uiEventLogger: UiEventLogger
|
@Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower
|
||||||
@Mock protected lateinit var dialogShower: UserSwitchDialogController.DialogShower
|
|
||||||
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
|
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
|
||||||
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
|
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
|
||||||
|
|
||||||
protected lateinit var underTest: UserInteractor
|
private lateinit var underTest: UserInteractor
|
||||||
|
|
||||||
protected lateinit var testCoroutineScope: TestCoroutineScope
|
private lateinit var testCoroutineScope: TestCoroutineScope
|
||||||
protected lateinit var userRepository: FakeUserRepository
|
private lateinit var userRepository: FakeUserRepository
|
||||||
protected lateinit var keyguardRepository: FakeKeyguardRepository
|
private lateinit var keyguardRepository: FakeKeyguardRepository
|
||||||
protected lateinit var telephonyRepository: FakeTelephonyRepository
|
private lateinit var telephonyRepository: FakeTelephonyRepository
|
||||||
|
|
||||||
abstract fun isRefactored(): Boolean
|
@Before
|
||||||
|
fun setUp() {
|
||||||
open fun setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
whenever(manager.getUserIcon(anyInt())).thenReturn(ICON)
|
||||||
|
whenever(manager.canAddMoreUsers(any())).thenReturn(true)
|
||||||
|
|
||||||
|
overrideResource(R.drawable.ic_account_circle, GUEST_ICON)
|
||||||
|
overrideResource(R.dimen.max_avatar_size, 10)
|
||||||
|
overrideResource(
|
||||||
|
com.android.internal.R.string.config_supervisedUserCreationPackage,
|
||||||
|
SUPERVISED_USER_CREATION_APP_PACKAGE,
|
||||||
|
)
|
||||||
|
|
||||||
userRepository = FakeUserRepository()
|
userRepository = FakeUserRepository()
|
||||||
keyguardRepository = FakeKeyguardRepository()
|
keyguardRepository = FakeKeyguardRepository()
|
||||||
@@ -79,16 +118,11 @@ abstract class UserInteractorTest : SysuiTestCase() {
|
|||||||
UserInteractor(
|
UserInteractor(
|
||||||
applicationContext = context,
|
applicationContext = context,
|
||||||
repository = userRepository,
|
repository = userRepository,
|
||||||
controller = controller,
|
|
||||||
activityStarter = activityStarter,
|
activityStarter = activityStarter,
|
||||||
keyguardInteractor =
|
keyguardInteractor =
|
||||||
KeyguardInteractor(
|
KeyguardInteractor(
|
||||||
repository = keyguardRepository,
|
repository = keyguardRepository,
|
||||||
),
|
),
|
||||||
featureFlags =
|
|
||||||
FakeFeatureFlags().apply {
|
|
||||||
set(Flags.USER_INTERACTOR_AND_REPO_USE_CONTROLLER, !isRefactored())
|
|
||||||
},
|
|
||||||
manager = manager,
|
manager = manager,
|
||||||
applicationScope = testCoroutineScope,
|
applicationScope = testCoroutineScope,
|
||||||
telephonyInteractor =
|
telephonyInteractor =
|
||||||
@@ -117,7 +151,665 @@ abstract class UserInteractorTest : SysuiTestCase() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `onRecordSelected - user`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 3, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
|
||||||
|
underTest.onRecordSelected(UserRecord(info = userInfos[1]), dialogShower)
|
||||||
|
|
||||||
|
verify(dialogShower).dismiss()
|
||||||
|
verify(activityManager).switchUser(userInfos[1].id)
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `onRecordSelected - switch to guest user`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 3, includeGuest = true)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
|
||||||
|
underTest.onRecordSelected(UserRecord(info = userInfos.last()))
|
||||||
|
|
||||||
|
verify(activityManager).switchUser(userInfos.last().id)
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `onRecordSelected - enter guest mode`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 3, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
val guestUserInfo = createUserInfo(id = 1337, name = "guest", isGuest = true)
|
||||||
|
whenever(manager.createGuest(any())).thenReturn(guestUserInfo)
|
||||||
|
|
||||||
|
underTest.onRecordSelected(UserRecord(isGuest = true), dialogShower)
|
||||||
|
|
||||||
|
verify(dialogShower).dismiss()
|
||||||
|
verify(manager).createGuest(any())
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `onRecordSelected - action`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 3, includeGuest = true)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
|
||||||
|
underTest.onRecordSelected(UserRecord(isAddSupervisedUser = true), dialogShower)
|
||||||
|
|
||||||
|
verify(dialogShower, never()).dismiss()
|
||||||
|
verify(activityStarter).startActivity(any(), anyBoolean())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `users - switcher enabled`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 3, includeGuest = true)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
|
||||||
|
var value: List<UserModel>? = null
|
||||||
|
val job = underTest.users.onEach { value = it }.launchIn(this)
|
||||||
|
assertUsers(models = value, count = 3, includeGuest = true)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `users - switches to second user`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
|
||||||
|
var value: List<UserModel>? = null
|
||||||
|
val job = underTest.users.onEach { value = it }.launchIn(this)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[1])
|
||||||
|
|
||||||
|
assertUsers(models = value, count = 2, selectedIndex = 1)
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `users - switcher not enabled`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = false))
|
||||||
|
|
||||||
|
var value: List<UserModel>? = null
|
||||||
|
val job = underTest.users.onEach { value = it }.launchIn(this)
|
||||||
|
assertUsers(models = value, count = 1)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun selectedUser() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
|
||||||
|
var value: UserModel? = null
|
||||||
|
val job = underTest.selectedUser.onEach { value = it }.launchIn(this)
|
||||||
|
assertUser(value, id = 0, isSelected = true)
|
||||||
|
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[1])
|
||||||
|
assertUser(value, id = 1, isSelected = true)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `actions - device unlocked`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
keyguardRepository.setKeyguardShowing(false)
|
||||||
|
var value: List<UserActionModel>? = null
|
||||||
|
val job = underTest.actions.onEach { value = it }.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(value)
|
||||||
|
.isEqualTo(
|
||||||
|
listOf(
|
||||||
|
UserActionModel.ENTER_GUEST_MODE,
|
||||||
|
UserActionModel.ADD_USER,
|
||||||
|
UserActionModel.ADD_SUPERVISED_USER,
|
||||||
|
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `actions - device unlocked user not primary - empty list`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[1])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
keyguardRepository.setKeyguardShowing(false)
|
||||||
|
var value: List<UserActionModel>? = null
|
||||||
|
val job = underTest.actions.onEach { value = it }.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(value).isEqualTo(emptyList<UserActionModel>())
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `actions - device unlocked user is guest - empty list`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = true)
|
||||||
|
assertThat(userInfos[1].isGuest).isTrue()
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[1])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
keyguardRepository.setKeyguardShowing(false)
|
||||||
|
var value: List<UserActionModel>? = null
|
||||||
|
val job = underTest.actions.onEach { value = it }.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(value).isEqualTo(emptyList<UserActionModel>())
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `actions - device locked add from lockscreen set - full list`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(
|
||||||
|
UserSwitcherSettingsModel(
|
||||||
|
isUserSwitcherEnabled = true,
|
||||||
|
isAddUsersFromLockscreen = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
keyguardRepository.setKeyguardShowing(false)
|
||||||
|
var value: List<UserActionModel>? = null
|
||||||
|
val job = underTest.actions.onEach { value = it }.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(value)
|
||||||
|
.isEqualTo(
|
||||||
|
listOf(
|
||||||
|
UserActionModel.ENTER_GUEST_MODE,
|
||||||
|
UserActionModel.ADD_USER,
|
||||||
|
UserActionModel.ADD_SUPERVISED_USER,
|
||||||
|
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `actions - device locked - only guest action and manage user is shown`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
keyguardRepository.setKeyguardShowing(true)
|
||||||
|
var value: List<UserActionModel>? = null
|
||||||
|
val job = underTest.actions.onEach { value = it }.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(value)
|
||||||
|
.isEqualTo(
|
||||||
|
listOf(
|
||||||
|
UserActionModel.ENTER_GUEST_MODE,
|
||||||
|
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `executeAction - add user - dialog shown`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
keyguardRepository.setKeyguardShowing(false)
|
||||||
|
var dialogRequest: ShowDialogRequestModel? = null
|
||||||
|
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
|
||||||
|
val dialogShower: UserSwitchDialogController.DialogShower = mock()
|
||||||
|
|
||||||
|
underTest.executeAction(UserActionModel.ADD_USER, dialogShower)
|
||||||
|
assertThat(dialogRequest)
|
||||||
|
.isEqualTo(
|
||||||
|
ShowDialogRequestModel.ShowAddUserDialog(
|
||||||
|
userHandle = userInfos[0].userHandle,
|
||||||
|
isKeyguardShowing = false,
|
||||||
|
showEphemeralMessage = false,
|
||||||
|
dialogShower = dialogShower,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
underTest.onDialogShown()
|
||||||
|
assertThat(dialogRequest).isNull()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `executeAction - add supervised user - starts activity`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)
|
||||||
|
|
||||||
|
val intentCaptor = kotlinArgumentCaptor<Intent>()
|
||||||
|
verify(activityStarter).startActivity(intentCaptor.capture(), eq(true))
|
||||||
|
assertThat(intentCaptor.value.action)
|
||||||
|
.isEqualTo(UserManager.ACTION_CREATE_SUPERVISED_USER)
|
||||||
|
assertThat(intentCaptor.value.`package`).isEqualTo(SUPERVISED_USER_CREATION_APP_PACKAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `executeAction - navigate to manage users`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
|
||||||
|
|
||||||
|
val intentCaptor = kotlinArgumentCaptor<Intent>()
|
||||||
|
verify(activityStarter).startActivity(intentCaptor.capture(), eq(true))
|
||||||
|
assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_USER_SETTINGS)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `executeAction - guest mode`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
val guestUserInfo = createUserInfo(id = 1337, name = "guest", isGuest = true)
|
||||||
|
whenever(manager.createGuest(any())).thenReturn(guestUserInfo)
|
||||||
|
val dialogRequests = mutableListOf<ShowDialogRequestModel?>()
|
||||||
|
val showDialogsJob =
|
||||||
|
underTest.dialogShowRequests
|
||||||
|
.onEach {
|
||||||
|
dialogRequests.add(it)
|
||||||
|
if (it != null) {
|
||||||
|
underTest.onDialogShown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.launchIn(this)
|
||||||
|
val dismissDialogsJob =
|
||||||
|
underTest.dialogDismissRequests
|
||||||
|
.onEach {
|
||||||
|
if (it != null) {
|
||||||
|
underTest.onDialogDismissed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
underTest.executeAction(UserActionModel.ENTER_GUEST_MODE)
|
||||||
|
|
||||||
|
assertThat(dialogRequests)
|
||||||
|
.contains(
|
||||||
|
ShowDialogRequestModel.ShowUserCreationDialog(isGuest = true),
|
||||||
|
)
|
||||||
|
verify(activityManager).switchUser(guestUserInfo.id)
|
||||||
|
|
||||||
|
showDialogsJob.cancel()
|
||||||
|
dismissDialogsJob.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `selectUser - already selected guest re-selected - exit guest dialog`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = true)
|
||||||
|
val guestUserInfo = userInfos[1]
|
||||||
|
assertThat(guestUserInfo.isGuest).isTrue()
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(guestUserInfo)
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
var dialogRequest: ShowDialogRequestModel? = null
|
||||||
|
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
|
||||||
|
|
||||||
|
underTest.selectUser(
|
||||||
|
newlySelectedUserId = guestUserInfo.id,
|
||||||
|
dialogShower = dialogShower,
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(dialogRequest)
|
||||||
|
.isInstanceOf(ShowDialogRequestModel.ShowExitGuestDialog::class.java)
|
||||||
|
verify(dialogShower, never()).dismiss()
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `selectUser - currently guest non-guest selected - exit guest dialog`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = true)
|
||||||
|
val guestUserInfo = userInfos[1]
|
||||||
|
assertThat(guestUserInfo.isGuest).isTrue()
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(guestUserInfo)
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
var dialogRequest: ShowDialogRequestModel? = null
|
||||||
|
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
|
||||||
|
|
||||||
|
underTest.selectUser(newlySelectedUserId = userInfos[0].id, dialogShower = dialogShower)
|
||||||
|
|
||||||
|
assertThat(dialogRequest)
|
||||||
|
.isInstanceOf(ShowDialogRequestModel.ShowExitGuestDialog::class.java)
|
||||||
|
verify(dialogShower, never()).dismiss()
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `selectUser - not currently guest - switches users`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
var dialogRequest: ShowDialogRequestModel? = null
|
||||||
|
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
|
||||||
|
|
||||||
|
underTest.selectUser(newlySelectedUserId = userInfos[1].id, dialogShower = dialogShower)
|
||||||
|
|
||||||
|
assertThat(dialogRequest).isNull()
|
||||||
|
verify(activityManager).switchUser(userInfos[1].id)
|
||||||
|
verify(dialogShower).dismiss()
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Telephony call state changes - refreshes users`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val refreshUsersCallCount = userRepository.refreshUsersCallCount
|
||||||
|
|
||||||
|
telephonyRepository.setCallState(1)
|
||||||
|
|
||||||
|
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `User switched broadcast`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
val callback1: UserInteractor.UserCallback = mock()
|
||||||
|
val callback2: UserInteractor.UserCallback = mock()
|
||||||
|
underTest.addCallback(callback1)
|
||||||
|
underTest.addCallback(callback2)
|
||||||
|
val refreshUsersCallCount = userRepository.refreshUsersCallCount
|
||||||
|
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[1])
|
||||||
|
fakeBroadcastDispatcher.registeredReceivers.forEach {
|
||||||
|
it.onReceive(
|
||||||
|
context,
|
||||||
|
Intent(Intent.ACTION_USER_SWITCHED)
|
||||||
|
.putExtra(Intent.EXTRA_USER_HANDLE, userInfos[1].id),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(callback1).onUserStateChanged()
|
||||||
|
verify(callback2).onUserStateChanged()
|
||||||
|
assertThat(userRepository.secondaryUserId).isEqualTo(userInfos[1].id)
|
||||||
|
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `User info changed broadcast`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
val refreshUsersCallCount = userRepository.refreshUsersCallCount
|
||||||
|
|
||||||
|
fakeBroadcastDispatcher.registeredReceivers.forEach {
|
||||||
|
it.onReceive(
|
||||||
|
context,
|
||||||
|
Intent(Intent.ACTION_USER_INFO_CHANGED),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `System user unlocked broadcast - refresh users`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
val refreshUsersCallCount = userRepository.refreshUsersCallCount
|
||||||
|
|
||||||
|
fakeBroadcastDispatcher.registeredReceivers.forEach {
|
||||||
|
it.onReceive(
|
||||||
|
context,
|
||||||
|
Intent(Intent.ACTION_USER_UNLOCKED)
|
||||||
|
.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Non-system user unlocked broadcast - do not refresh users`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 2, includeGuest = false)
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
val refreshUsersCallCount = userRepository.refreshUsersCallCount
|
||||||
|
|
||||||
|
fakeBroadcastDispatcher.registeredReceivers.forEach {
|
||||||
|
it.onReceive(
|
||||||
|
context,
|
||||||
|
Intent(Intent.ACTION_USER_UNLOCKED).putExtra(Intent.EXTRA_USER_HANDLE, 1337),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun userRecords() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 3, includeGuest = false)
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
keyguardRepository.setKeyguardShowing(false)
|
||||||
|
|
||||||
|
testCoroutineScope.advanceUntilIdle()
|
||||||
|
|
||||||
|
assertRecords(
|
||||||
|
records = underTest.userRecords.value,
|
||||||
|
userIds = listOf(0, 1, 2),
|
||||||
|
selectedUserIndex = 0,
|
||||||
|
includeGuest = false,
|
||||||
|
expectedActions =
|
||||||
|
listOf(
|
||||||
|
UserActionModel.ENTER_GUEST_MODE,
|
||||||
|
UserActionModel.ADD_USER,
|
||||||
|
UserActionModel.ADD_SUPERVISED_USER,
|
||||||
|
UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun selectedUserRecord() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
val userInfos = createUserInfos(count = 3, includeGuest = true)
|
||||||
|
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
|
||||||
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
keyguardRepository.setKeyguardShowing(false)
|
||||||
|
|
||||||
|
assertRecordForUser(
|
||||||
|
record = underTest.selectedUserRecord.value,
|
||||||
|
id = 0,
|
||||||
|
hasPicture = true,
|
||||||
|
isCurrent = true,
|
||||||
|
isSwitchToEnabled = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertUsers(
|
||||||
|
models: List<UserModel>?,
|
||||||
|
count: Int,
|
||||||
|
selectedIndex: Int = 0,
|
||||||
|
includeGuest: Boolean = false,
|
||||||
|
) {
|
||||||
|
checkNotNull(models)
|
||||||
|
assertThat(models.size).isEqualTo(count)
|
||||||
|
models.forEachIndexed { index, model ->
|
||||||
|
assertUser(
|
||||||
|
model = model,
|
||||||
|
id = index,
|
||||||
|
isSelected = index == selectedIndex,
|
||||||
|
isGuest = includeGuest && index == count - 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertUser(
|
||||||
|
model: UserModel?,
|
||||||
|
id: Int,
|
||||||
|
isSelected: Boolean = false,
|
||||||
|
isGuest: Boolean = false,
|
||||||
|
) {
|
||||||
|
checkNotNull(model)
|
||||||
|
assertThat(model.id).isEqualTo(id)
|
||||||
|
assertThat(model.name).isEqualTo(Text.Loaded(if (isGuest) "guest" else "user_$id"))
|
||||||
|
assertThat(model.isSelected).isEqualTo(isSelected)
|
||||||
|
assertThat(model.isSelectable).isTrue()
|
||||||
|
assertThat(model.isGuest).isEqualTo(isGuest)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertRecords(
|
||||||
|
records: List<UserRecord>,
|
||||||
|
userIds: List<Int>,
|
||||||
|
selectedUserIndex: Int = 0,
|
||||||
|
includeGuest: Boolean = false,
|
||||||
|
expectedActions: List<UserActionModel> = emptyList(),
|
||||||
|
) {
|
||||||
|
assertThat(records.size >= userIds.size).isTrue()
|
||||||
|
userIds.indices.forEach { userIndex ->
|
||||||
|
val record = records[userIndex]
|
||||||
|
assertThat(record.info).isNotNull()
|
||||||
|
val isGuest = includeGuest && userIndex == userIds.size - 1
|
||||||
|
assertRecordForUser(
|
||||||
|
record = record,
|
||||||
|
id = userIds[userIndex],
|
||||||
|
hasPicture = !isGuest,
|
||||||
|
isCurrent = userIndex == selectedUserIndex,
|
||||||
|
isGuest = isGuest,
|
||||||
|
isSwitchToEnabled = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(records.size - userIds.size).isEqualTo(expectedActions.size)
|
||||||
|
(userIds.size until userIds.size + expectedActions.size).forEach { actionIndex ->
|
||||||
|
val record = records[actionIndex]
|
||||||
|
assertThat(record.info).isNull()
|
||||||
|
assertRecordForAction(
|
||||||
|
record = record,
|
||||||
|
type = expectedActions[actionIndex - userIds.size],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertRecordForUser(
|
||||||
|
record: UserRecord?,
|
||||||
|
id: Int? = null,
|
||||||
|
hasPicture: Boolean = false,
|
||||||
|
isCurrent: Boolean = false,
|
||||||
|
isGuest: Boolean = false,
|
||||||
|
isSwitchToEnabled: Boolean = false,
|
||||||
|
) {
|
||||||
|
checkNotNull(record)
|
||||||
|
assertThat(record.info?.id).isEqualTo(id)
|
||||||
|
assertThat(record.picture != null).isEqualTo(hasPicture)
|
||||||
|
assertThat(record.isCurrent).isEqualTo(isCurrent)
|
||||||
|
assertThat(record.isGuest).isEqualTo(isGuest)
|
||||||
|
assertThat(record.isSwitchToEnabled).isEqualTo(isSwitchToEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertRecordForAction(
|
||||||
|
record: UserRecord,
|
||||||
|
type: UserActionModel,
|
||||||
|
) {
|
||||||
|
assertThat(record.isGuest).isEqualTo(type == UserActionModel.ENTER_GUEST_MODE)
|
||||||
|
assertThat(record.isAddUser).isEqualTo(type == UserActionModel.ADD_USER)
|
||||||
|
assertThat(record.isAddSupervisedUser)
|
||||||
|
.isEqualTo(type == UserActionModel.ADD_SUPERVISED_USER)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createUserInfos(
|
||||||
|
count: Int,
|
||||||
|
includeGuest: Boolean,
|
||||||
|
): List<UserInfo> {
|
||||||
|
return (0 until count).map { index ->
|
||||||
|
val isGuest = includeGuest && index == count - 1
|
||||||
|
createUserInfo(
|
||||||
|
id = index,
|
||||||
|
name =
|
||||||
|
if (isGuest) {
|
||||||
|
"guest"
|
||||||
|
} else {
|
||||||
|
"user_$index"
|
||||||
|
},
|
||||||
|
isPrimary = !isGuest && index == 0,
|
||||||
|
isGuest = isGuest,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createUserInfo(
|
||||||
|
id: Int,
|
||||||
|
name: String,
|
||||||
|
isPrimary: Boolean = false,
|
||||||
|
isGuest: Boolean = false,
|
||||||
|
): UserInfo {
|
||||||
|
return UserInfo(
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
/* iconPath= */ "",
|
||||||
|
/* flags= */ if (isPrimary) {
|
||||||
|
UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
if (isGuest) {
|
||||||
|
UserManager.USER_TYPE_FULL_GUEST
|
||||||
|
} else {
|
||||||
|
UserManager.USER_TYPE_FULL_SYSTEM
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||||
|
private val ICON = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
|
||||||
|
private val GUEST_ICON: Drawable = mock()
|
||||||
|
private const val SUPERVISED_USER_CREATION_APP_PACKAGE = "supervisedUserCreation"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,174 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.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.Mockito.anyBoolean
|
|
||||||
import org.mockito.Mockito.verify
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
@RunWith(JUnit4::class)
|
|
||||||
open class UserInteractorUnrefactoredTest : UserInteractorTest() {
|
|
||||||
|
|
||||||
override fun isRefactored(): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
override fun setUp() {
|
|
||||||
super.setUp()
|
|
||||||
}
|
|
||||||
|
|
||||||
@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) {
|
|
||||||
setActions()
|
|
||||||
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) {
|
|
||||||
setActions()
|
|
||||||
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) {
|
|
||||||
setActions()
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setActions() {
|
|
||||||
userRepository.setActions(UserActionModel.values().toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,7 @@ package com.android.systemui.user.ui.viewmodel
|
|||||||
|
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.app.admin.DevicePolicyManager
|
import android.app.admin.DevicePolicyManager
|
||||||
import android.graphics.drawable.Drawable
|
import android.content.pm.UserInfo
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.internal.logging.UiEventLogger
|
import com.android.internal.logging.UiEventLogger
|
||||||
@@ -27,32 +27,37 @@ import com.android.systemui.GuestResetOrExitSessionReceiver
|
|||||||
import com.android.systemui.GuestResumeSessionReceiver
|
import com.android.systemui.GuestResumeSessionReceiver
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.common.shared.model.Text
|
import com.android.systemui.common.shared.model.Text
|
||||||
import com.android.systemui.flags.FakeFeatureFlags
|
|
||||||
import com.android.systemui.flags.Flags
|
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.power.data.repository.FakePowerRepository
|
import com.android.systemui.power.data.repository.FakePowerRepository
|
||||||
import com.android.systemui.power.domain.interactor.PowerInteractor
|
import com.android.systemui.power.domain.interactor.PowerInteractor
|
||||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController
|
import com.android.systemui.statusbar.policy.DeviceProvisionedController
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
|
||||||
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
|
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
|
||||||
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
|
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
|
||||||
|
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
||||||
import com.android.systemui.user.data.repository.FakeUserRepository
|
import com.android.systemui.user.data.repository.FakeUserRepository
|
||||||
import com.android.systemui.user.domain.interactor.GuestUserInteractor
|
import com.android.systemui.user.domain.interactor.GuestUserInteractor
|
||||||
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
|
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
|
||||||
import com.android.systemui.user.domain.interactor.UserInteractor
|
import com.android.systemui.user.domain.interactor.UserInteractor
|
||||||
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
|
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
|
||||||
import com.android.systemui.user.shared.model.UserActionModel
|
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.mock
|
import com.android.systemui.util.mockito.whenever
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancelAndJoin
|
||||||
|
import kotlinx.coroutines.flow.toList
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.test.TestCoroutineScope
|
import kotlinx.coroutines.test.TestDispatcher
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.test.TestResult
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -60,11 +65,11 @@ import org.junit.runners.JUnit4
|
|||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
class UserSwitcherViewModelTest : SysuiTestCase() {
|
class UserSwitcherViewModelTest : SysuiTestCase() {
|
||||||
|
|
||||||
@Mock private lateinit var controller: UserSwitcherController
|
|
||||||
@Mock private lateinit var activityStarter: ActivityStarter
|
@Mock private lateinit var activityStarter: ActivityStarter
|
||||||
@Mock private lateinit var activityManager: ActivityManager
|
@Mock private lateinit var activityManager: ActivityManager
|
||||||
@Mock private lateinit var manager: UserManager
|
@Mock private lateinit var manager: UserManager
|
||||||
@@ -80,28 +85,47 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
|
|||||||
private lateinit var keyguardRepository: FakeKeyguardRepository
|
private lateinit var keyguardRepository: FakeKeyguardRepository
|
||||||
private lateinit var powerRepository: FakePowerRepository
|
private lateinit var powerRepository: FakePowerRepository
|
||||||
|
|
||||||
|
private lateinit var testDispatcher: TestDispatcher
|
||||||
|
private lateinit var testScope: TestScope
|
||||||
|
private lateinit var injectedScope: CoroutineScope
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
whenever(manager.canAddMoreUsers(any())).thenReturn(true)
|
||||||
|
whenever(manager.getUserSwitchability(any()))
|
||||||
|
.thenReturn(UserManager.SWITCHABILITY_STATUS_OK)
|
||||||
|
overrideResource(
|
||||||
|
com.android.internal.R.string.config_supervisedUserCreationPackage,
|
||||||
|
SUPERVISED_USER_CREATION_PACKAGE,
|
||||||
|
)
|
||||||
|
|
||||||
|
testDispatcher = UnconfinedTestDispatcher()
|
||||||
|
testScope = TestScope(testDispatcher)
|
||||||
|
injectedScope = CoroutineScope(testScope.coroutineContext + SupervisorJob())
|
||||||
userRepository = FakeUserRepository()
|
userRepository = FakeUserRepository()
|
||||||
|
runBlocking {
|
||||||
|
userRepository.setSettings(
|
||||||
|
UserSwitcherSettingsModel(
|
||||||
|
isUserSwitcherEnabled = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
keyguardRepository = FakeKeyguardRepository()
|
keyguardRepository = FakeKeyguardRepository()
|
||||||
powerRepository = FakePowerRepository()
|
powerRepository = FakePowerRepository()
|
||||||
val featureFlags = FakeFeatureFlags()
|
|
||||||
featureFlags.set(Flags.USER_INTERACTOR_AND_REPO_USE_CONTROLLER, true)
|
|
||||||
val scope = TestCoroutineScope()
|
|
||||||
val refreshUsersScheduler =
|
val refreshUsersScheduler =
|
||||||
RefreshUsersScheduler(
|
RefreshUsersScheduler(
|
||||||
applicationScope = scope,
|
applicationScope = injectedScope,
|
||||||
mainDispatcher = IMMEDIATE,
|
mainDispatcher = testDispatcher,
|
||||||
repository = userRepository,
|
repository = userRepository,
|
||||||
)
|
)
|
||||||
val guestUserInteractor =
|
val guestUserInteractor =
|
||||||
GuestUserInteractor(
|
GuestUserInteractor(
|
||||||
applicationContext = context,
|
applicationContext = context,
|
||||||
applicationScope = scope,
|
applicationScope = injectedScope,
|
||||||
mainDispatcher = IMMEDIATE,
|
mainDispatcher = testDispatcher,
|
||||||
backgroundDispatcher = IMMEDIATE,
|
backgroundDispatcher = testDispatcher,
|
||||||
manager = manager,
|
manager = manager,
|
||||||
repository = userRepository,
|
repository = userRepository,
|
||||||
deviceProvisionedController = deviceProvisionedController,
|
deviceProvisionedController = deviceProvisionedController,
|
||||||
@@ -118,21 +142,19 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
|
|||||||
UserInteractor(
|
UserInteractor(
|
||||||
applicationContext = context,
|
applicationContext = context,
|
||||||
repository = userRepository,
|
repository = userRepository,
|
||||||
controller = controller,
|
|
||||||
activityStarter = activityStarter,
|
activityStarter = activityStarter,
|
||||||
keyguardInteractor =
|
keyguardInteractor =
|
||||||
KeyguardInteractor(
|
KeyguardInteractor(
|
||||||
repository = keyguardRepository,
|
repository = keyguardRepository,
|
||||||
),
|
),
|
||||||
featureFlags = featureFlags,
|
|
||||||
manager = manager,
|
manager = manager,
|
||||||
applicationScope = scope,
|
applicationScope = injectedScope,
|
||||||
telephonyInteractor =
|
telephonyInteractor =
|
||||||
TelephonyInteractor(
|
TelephonyInteractor(
|
||||||
repository = FakeTelephonyRepository(),
|
repository = FakeTelephonyRepository(),
|
||||||
),
|
),
|
||||||
broadcastDispatcher = fakeBroadcastDispatcher,
|
broadcastDispatcher = fakeBroadcastDispatcher,
|
||||||
backgroundDispatcher = IMMEDIATE,
|
backgroundDispatcher = testDispatcher,
|
||||||
activityManager = activityManager,
|
activityManager = activityManager,
|
||||||
refreshUsersScheduler = refreshUsersScheduler,
|
refreshUsersScheduler = refreshUsersScheduler,
|
||||||
guestUserInteractor = guestUserInteractor,
|
guestUserInteractor = guestUserInteractor,
|
||||||
@@ -141,146 +163,133 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
|
|||||||
PowerInteractor(
|
PowerInteractor(
|
||||||
repository = powerRepository,
|
repository = powerRepository,
|
||||||
),
|
),
|
||||||
featureFlags = featureFlags,
|
|
||||||
guestUserInteractor = guestUserInteractor,
|
guestUserInteractor = guestUserInteractor,
|
||||||
)
|
)
|
||||||
.create(UserSwitcherViewModel::class.java)
|
.create(UserSwitcherViewModel::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun users() =
|
fun users() = selfCancelingTest {
|
||||||
runBlocking(IMMEDIATE) {
|
val userInfos =
|
||||||
userRepository.setUsers(
|
|
||||||
listOf(
|
listOf(
|
||||||
UserModel(
|
UserInfo(
|
||||||
id = 0,
|
/* id= */ 0,
|
||||||
name = Text.Loaded("zero"),
|
/* name= */ "zero",
|
||||||
image = USER_IMAGE,
|
/* iconPath= */ "",
|
||||||
isSelected = true,
|
/* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN,
|
||||||
isSelectable = true,
|
UserManager.USER_TYPE_FULL_SYSTEM,
|
||||||
isGuest = false,
|
|
||||||
),
|
),
|
||||||
UserModel(
|
UserInfo(
|
||||||
id = 1,
|
/* id= */ 1,
|
||||||
name = Text.Loaded("one"),
|
/* name= */ "one",
|
||||||
image = USER_IMAGE,
|
/* iconPath= */ "",
|
||||||
isSelected = false,
|
/* flags= */ 0,
|
||||||
isSelectable = true,
|
UserManager.USER_TYPE_FULL_SYSTEM,
|
||||||
isGuest = false,
|
|
||||||
),
|
),
|
||||||
UserModel(
|
UserInfo(
|
||||||
id = 2,
|
/* id= */ 2,
|
||||||
name = Text.Loaded("two"),
|
/* name= */ "two",
|
||||||
image = USER_IMAGE,
|
/* iconPath= */ "",
|
||||||
isSelected = false,
|
/* flags= */ 0,
|
||||||
isSelectable = false,
|
UserManager.USER_TYPE_FULL_SYSTEM,
|
||||||
isGuest = false,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
userRepository.setUserInfos(userInfos)
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
|
||||||
var userViewModels: List<UserViewModel>? = null
|
val userViewModels = mutableListOf<List<UserViewModel>>()
|
||||||
val job = underTest.users.onEach { userViewModels = it }.launchIn(this)
|
val job = launch(testDispatcher) { underTest.users.toList(userViewModels) }
|
||||||
|
|
||||||
assertThat(userViewModels).hasSize(3)
|
assertThat(userViewModels.last()).hasSize(3)
|
||||||
assertUserViewModel(
|
assertUserViewModel(
|
||||||
viewModel = userViewModels?.get(0),
|
viewModel = userViewModels.last()[0],
|
||||||
viewKey = 0,
|
viewKey = 0,
|
||||||
name = "zero",
|
name = "zero",
|
||||||
isSelectionMarkerVisible = true,
|
isSelectionMarkerVisible = true,
|
||||||
alpha = LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA,
|
|
||||||
isClickable = true,
|
|
||||||
)
|
)
|
||||||
assertUserViewModel(
|
assertUserViewModel(
|
||||||
viewModel = userViewModels?.get(1),
|
viewModel = userViewModels.last()[1],
|
||||||
viewKey = 1,
|
viewKey = 1,
|
||||||
name = "one",
|
name = "one",
|
||||||
isSelectionMarkerVisible = false,
|
isSelectionMarkerVisible = false,
|
||||||
alpha = LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA,
|
|
||||||
isClickable = true,
|
|
||||||
)
|
)
|
||||||
assertUserViewModel(
|
assertUserViewModel(
|
||||||
viewModel = userViewModels?.get(2),
|
viewModel = userViewModels.last()[2],
|
||||||
viewKey = 2,
|
viewKey = 2,
|
||||||
name = "two",
|
name = "two",
|
||||||
isSelectionMarkerVisible = false,
|
isSelectionMarkerVisible = false,
|
||||||
alpha = LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA,
|
|
||||||
isClickable = false,
|
|
||||||
)
|
)
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `maximumUserColumns - few users`() =
|
fun `maximumUserColumns - few users`() = selfCancelingTest {
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
setUsers(count = 2)
|
setUsers(count = 2)
|
||||||
var value: Int? = null
|
val values = mutableListOf<Int>()
|
||||||
val job = underTest.maximumUserColumns.onEach { value = it }.launchIn(this)
|
val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) }
|
||||||
|
|
||||||
|
assertThat(values.last()).isEqualTo(4)
|
||||||
|
|
||||||
assertThat(value).isEqualTo(4)
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `maximumUserColumns - many users`() =
|
fun `maximumUserColumns - many users`() = selfCancelingTest {
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
setUsers(count = 5)
|
setUsers(count = 5)
|
||||||
var value: Int? = null
|
val values = mutableListOf<Int>()
|
||||||
val job = underTest.maximumUserColumns.onEach { value = it }.launchIn(this)
|
val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) }
|
||||||
|
|
||||||
assertThat(value).isEqualTo(3)
|
assertThat(values.last()).isEqualTo(3)
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `isOpenMenuButtonVisible - has actions - true`() =
|
fun `isOpenMenuButtonVisible - has actions - true`() = selfCancelingTest {
|
||||||
runBlocking(IMMEDIATE) {
|
setUsers(2)
|
||||||
userRepository.setActions(UserActionModel.values().toList())
|
|
||||||
|
|
||||||
var isVisible: Boolean? = null
|
val isVisible = mutableListOf<Boolean>()
|
||||||
val job = underTest.isOpenMenuButtonVisible.onEach { isVisible = it }.launchIn(this)
|
val job = launch(testDispatcher) { underTest.isOpenMenuButtonVisible.toList(isVisible) }
|
||||||
|
|
||||||
assertThat(isVisible).isTrue()
|
assertThat(isVisible.last()).isTrue()
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `isOpenMenuButtonVisible - no actions - false`() =
|
fun `isOpenMenuButtonVisible - no actions - false`() = selfCancelingTest {
|
||||||
runBlocking(IMMEDIATE) {
|
val userInfos = setUsers(2)
|
||||||
userRepository.setActions(emptyList())
|
userRepository.setSelectedUserInfo(userInfos[1])
|
||||||
|
keyguardRepository.setKeyguardShowing(true)
|
||||||
|
whenever(manager.canAddMoreUsers(any())).thenReturn(false)
|
||||||
|
|
||||||
var isVisible: Boolean? = null
|
val isVisible = mutableListOf<Boolean>()
|
||||||
val job = underTest.isOpenMenuButtonVisible.onEach { isVisible = it }.launchIn(this)
|
val job = launch(testDispatcher) { underTest.isOpenMenuButtonVisible.toList(isVisible) }
|
||||||
|
|
||||||
assertThat(isVisible).isFalse()
|
assertThat(isVisible.last()).isFalse()
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun menu() =
|
fun menu() = selfCancelingTest {
|
||||||
runBlocking(IMMEDIATE) {
|
val isMenuVisible = mutableListOf<Boolean>()
|
||||||
userRepository.setActions(UserActionModel.values().toList())
|
val job = launch(testDispatcher) { underTest.isMenuVisible.toList(isMenuVisible) }
|
||||||
var isMenuVisible: Boolean? = null
|
assertThat(isMenuVisible.last()).isFalse()
|
||||||
val job = underTest.isMenuVisible.onEach { isMenuVisible = it }.launchIn(this)
|
|
||||||
assertThat(isMenuVisible).isFalse()
|
|
||||||
|
|
||||||
underTest.onOpenMenuButtonClicked()
|
underTest.onOpenMenuButtonClicked()
|
||||||
assertThat(isMenuVisible).isTrue()
|
assertThat(isMenuVisible.last()).isTrue()
|
||||||
|
|
||||||
underTest.onMenuClosed()
|
underTest.onMenuClosed()
|
||||||
assertThat(isMenuVisible).isFalse()
|
assertThat(isMenuVisible.last()).isFalse()
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `menu actions`() =
|
fun `menu actions`() = selfCancelingTest {
|
||||||
runBlocking(IMMEDIATE) {
|
setUsers(2)
|
||||||
userRepository.setActions(UserActionModel.values().toList())
|
val actions = mutableListOf<List<UserActionViewModel>>()
|
||||||
var actions: List<UserActionViewModel>? = null
|
val job = launch(testDispatcher) { underTest.menu.toList(actions) }
|
||||||
val job = underTest.menu.onEach { actions = it }.launchIn(this)
|
|
||||||
|
|
||||||
assertThat(actions?.map { it.viewKey })
|
assertThat(actions.last().map { it.viewKey })
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
listOf(
|
listOf(
|
||||||
UserActionModel.ENTER_GUEST_MODE.ordinal.toLong(),
|
UserActionModel.ENTER_GUEST_MODE.ordinal.toLong(),
|
||||||
@@ -294,69 +303,76 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `isFinishRequested - finishes when user is switched`() =
|
fun `isFinishRequested - finishes when user is switched`() = selfCancelingTest {
|
||||||
runBlocking(IMMEDIATE) {
|
val userInfos = setUsers(count = 2)
|
||||||
setUsers(count = 2)
|
val isFinishRequested = mutableListOf<Boolean>()
|
||||||
var isFinishRequested: Boolean? = null
|
val job = launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
|
||||||
val job = underTest.isFinishRequested.onEach { isFinishRequested = it }.launchIn(this)
|
assertThat(isFinishRequested.last()).isFalse()
|
||||||
assertThat(isFinishRequested).isFalse()
|
|
||||||
|
|
||||||
userRepository.setSelectedUser(1)
|
userRepository.setSelectedUserInfo(userInfos[1])
|
||||||
yield()
|
|
||||||
assertThat(isFinishRequested).isTrue()
|
assertThat(isFinishRequested.last()).isTrue()
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `isFinishRequested - finishes when the screen turns off`() =
|
fun `isFinishRequested - finishes when the screen turns off`() = selfCancelingTest {
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
setUsers(count = 2)
|
setUsers(count = 2)
|
||||||
powerRepository.setInteractive(true)
|
powerRepository.setInteractive(true)
|
||||||
var isFinishRequested: Boolean? = null
|
val isFinishRequested = mutableListOf<Boolean>()
|
||||||
val job = underTest.isFinishRequested.onEach { isFinishRequested = it }.launchIn(this)
|
val job = launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
|
||||||
assertThat(isFinishRequested).isFalse()
|
assertThat(isFinishRequested.last()).isFalse()
|
||||||
|
|
||||||
powerRepository.setInteractive(false)
|
powerRepository.setInteractive(false)
|
||||||
yield()
|
|
||||||
assertThat(isFinishRequested).isTrue()
|
assertThat(isFinishRequested.last()).isTrue()
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `isFinishRequested - finishes when cancel button is clicked`() =
|
fun `isFinishRequested - finishes when cancel button is clicked`() = selfCancelingTest {
|
||||||
runBlocking(IMMEDIATE) {
|
|
||||||
setUsers(count = 2)
|
setUsers(count = 2)
|
||||||
powerRepository.setInteractive(true)
|
powerRepository.setInteractive(true)
|
||||||
var isFinishRequested: Boolean? = null
|
val isFinishRequested = mutableListOf<Boolean>()
|
||||||
val job = underTest.isFinishRequested.onEach { isFinishRequested = it }.launchIn(this)
|
val job = launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
|
||||||
assertThat(isFinishRequested).isFalse()
|
assertThat(isFinishRequested.last()).isFalse()
|
||||||
|
|
||||||
underTest.onCancelButtonClicked()
|
underTest.onCancelButtonClicked()
|
||||||
yield()
|
|
||||||
assertThat(isFinishRequested).isTrue()
|
assertThat(isFinishRequested.last()).isTrue()
|
||||||
|
|
||||||
underTest.onFinished()
|
underTest.onFinished()
|
||||||
yield()
|
|
||||||
assertThat(isFinishRequested).isFalse()
|
assertThat(isFinishRequested.last()).isFalse()
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setUsers(count: Int) {
|
private suspend fun setUsers(count: Int): List<UserInfo> {
|
||||||
userRepository.setUsers(
|
val userInfos =
|
||||||
(0 until count).map { index ->
|
(0 until count).map { index ->
|
||||||
UserModel(
|
UserInfo(
|
||||||
id = index,
|
/* id= */ index,
|
||||||
name = Text.Loaded("$index"),
|
/* name= */ "$index",
|
||||||
image = USER_IMAGE,
|
/* iconPath= */ "",
|
||||||
isSelected = index == 0,
|
/* flags= */ if (index == 0) {
|
||||||
isSelectable = true,
|
// This is the primary user.
|
||||||
isGuest = false,
|
UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN
|
||||||
|
} else {
|
||||||
|
// This isn't the primary user.
|
||||||
|
0
|
||||||
|
},
|
||||||
|
UserManager.USER_TYPE_FULL_SYSTEM,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
userRepository.setUserInfos(userInfos)
|
||||||
|
|
||||||
|
if (userInfos.isNotEmpty()) {
|
||||||
|
userRepository.setSelectedUserInfo(userInfos[0])
|
||||||
|
}
|
||||||
|
return userInfos
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assertUserViewModel(
|
private fun assertUserViewModel(
|
||||||
@@ -364,19 +380,25 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
|
|||||||
viewKey: Int,
|
viewKey: Int,
|
||||||
name: String,
|
name: String,
|
||||||
isSelectionMarkerVisible: Boolean,
|
isSelectionMarkerVisible: Boolean,
|
||||||
alpha: Float,
|
|
||||||
isClickable: Boolean,
|
|
||||||
) {
|
) {
|
||||||
checkNotNull(viewModel)
|
checkNotNull(viewModel)
|
||||||
assertThat(viewModel.viewKey).isEqualTo(viewKey)
|
assertThat(viewModel.viewKey).isEqualTo(viewKey)
|
||||||
assertThat(viewModel.name).isEqualTo(Text.Loaded(name))
|
assertThat(viewModel.name).isEqualTo(Text.Loaded(name))
|
||||||
assertThat(viewModel.isSelectionMarkerVisible).isEqualTo(isSelectionMarkerVisible)
|
assertThat(viewModel.isSelectionMarkerVisible).isEqualTo(isSelectionMarkerVisible)
|
||||||
assertThat(viewModel.alpha).isEqualTo(alpha)
|
assertThat(viewModel.alpha)
|
||||||
assertThat(viewModel.onClicked != null).isEqualTo(isClickable)
|
.isEqualTo(LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA)
|
||||||
|
assertThat(viewModel.onClicked).isNotNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selfCancelingTest(
|
||||||
|
block: suspend TestScope.() -> Unit,
|
||||||
|
): TestResult =
|
||||||
|
testScope.runTest {
|
||||||
|
block()
|
||||||
|
injectedScope.coroutineContext[Job.Key]?.cancelAndJoin()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
private const val SUPERVISED_USER_CREATION_PACKAGE = "com.some.package"
|
||||||
private val USER_IMAGE = mock<Drawable>()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,26 +20,15 @@ package com.android.systemui.user.data.repository
|
|||||||
import android.content.pm.UserInfo
|
import android.content.pm.UserInfo
|
||||||
import android.os.UserHandle
|
import android.os.UserHandle
|
||||||
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
|
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 java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.yield
|
||||||
|
|
||||||
class FakeUserRepository : UserRepository {
|
class FakeUserRepository : UserRepository {
|
||||||
|
|
||||||
private val _users = MutableStateFlow<List<UserModel>>(emptyList())
|
|
||||||
override val users: Flow<List<UserModel>> = _users.asStateFlow()
|
|
||||||
override val selectedUser: Flow<UserModel> =
|
|
||||||
users.map { models -> models.first { model -> model.isSelected } }
|
|
||||||
|
|
||||||
private val _actions = MutableStateFlow<List<UserActionModel>>(emptyList())
|
|
||||||
override val actions: Flow<List<UserActionModel>> = _actions.asStateFlow()
|
|
||||||
|
|
||||||
private val _userSwitcherSettings = MutableStateFlow(UserSwitcherSettingsModel())
|
private val _userSwitcherSettings = MutableStateFlow(UserSwitcherSettingsModel())
|
||||||
override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> =
|
override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> =
|
||||||
_userSwitcherSettings.asStateFlow()
|
_userSwitcherSettings.asStateFlow()
|
||||||
@@ -52,9 +41,6 @@ class FakeUserRepository : UserRepository {
|
|||||||
|
|
||||||
override var lastSelectedNonGuestUserId: Int = UserHandle.USER_SYSTEM
|
override var lastSelectedNonGuestUserId: Int = UserHandle.USER_SYSTEM
|
||||||
|
|
||||||
private val _isActionableWhenLocked = MutableStateFlow(false)
|
|
||||||
override val isActionableWhenLocked: Flow<Boolean> = _isActionableWhenLocked.asStateFlow()
|
|
||||||
|
|
||||||
private var _isGuestUserAutoCreated: Boolean = false
|
private var _isGuestUserAutoCreated: Boolean = false
|
||||||
override val isGuestUserAutoCreated: Boolean
|
override val isGuestUserAutoCreated: Boolean
|
||||||
get() = _isGuestUserAutoCreated
|
get() = _isGuestUserAutoCreated
|
||||||
@@ -100,35 +86,6 @@ class FakeUserRepository : UserRepository {
|
|||||||
yield()
|
yield()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setUsers(models: List<UserModel>) {
|
|
||||||
_users.value = models
|
|
||||||
}
|
|
||||||
|
|
||||||
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!"
|
|
||||||
}
|
|
||||||
|
|
||||||
setUsers(
|
|
||||||
_users.value.map { model ->
|
|
||||||
when {
|
|
||||||
model.isSelected && model.id != userId -> model.copy(isSelected = false)
|
|
||||||
!model.isSelected && model.id == userId -> model.copy(isSelected = true)
|
|
||||||
else -> model
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
yield()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setActions(models: List<UserActionModel>) {
|
|
||||||
_actions.value = models
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setActionableWhenLocked(value: Boolean) {
|
|
||||||
_isActionableWhenLocked.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setGuestUserAutoCreated(value: Boolean) {
|
fun setGuestUserAutoCreated(value: Boolean) {
|
||||||
_isGuestUserAutoCreated = value
|
_isGuestUserAutoCreated = value
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user