Merge "[flexiglass] Hooks up real app state and logic in keyguard (1)" into udc-qpr-dev

This commit is contained in:
TreeHugger Robot
2023-06-26 14:13:07 +00:00
committed by Android (Google) Code Review
22 changed files with 521 additions and 414 deletions

View File

@@ -16,8 +16,11 @@
package com.android.systemui.authentication
import com.android.keyguard.KeyguardSecurityModel
import com.android.systemui.authentication.data.repository.AuthenticationRepositoryModule
import dagger.Module
import dagger.Provides
import java.util.function.Function
@Module(
includes =
@@ -25,4 +28,12 @@ import dagger.Module
AuthenticationRepositoryModule::class,
],
)
object AuthenticationModule
object AuthenticationModule {
@Provides
fun getSecurityMode(
model: KeyguardSecurityModel,
): Function<Int, KeyguardSecurityModel.SecurityMode> {
return Function { userId -> model.getSecurityMode(userId) }
}
}

View File

@@ -16,13 +16,20 @@
package com.android.systemui.authentication.data.repository
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.user.data.repository.UserRepository
import dagger.Binds
import dagger.Module
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.withContext
import java.util.function.Function
import javax.inject.Inject
/** Defines interface for classes that can access authentication-related application state. */
interface AuthenticationRepository {
@@ -37,12 +44,6 @@ interface AuthenticationRepository {
*/
val isUnlocked: StateFlow<Boolean>
/**
* The currently-configured authentication method. This determines how the authentication
* challenge is completed in order to unlock an otherwise locked device.
*/
val authenticationMethod: StateFlow<AuthenticationMethodModel>
/**
* Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
* dismisses once the authentication challenge is completed. For example, completing a biometric
@@ -57,12 +58,15 @@ interface AuthenticationRepository {
*/
val failedAuthenticationAttempts: StateFlow<Int>
/**
* Returns the currently-configured authentication method. This determines how the
* authentication challenge is completed in order to unlock an otherwise locked device.
*/
suspend fun getAuthenticationMethod(): AuthenticationMethodModel
/** See [isUnlocked]. */
fun setUnlocked(isUnlocked: Boolean)
/** See [authenticationMethod]. */
fun setAuthenticationMethod(authenticationMethod: AuthenticationMethodModel)
/** See [isBypassEnabled]. */
fun setBypassEnabled(isBypassEnabled: Boolean)
@@ -70,19 +74,18 @@ interface AuthenticationRepository {
fun setFailedAuthenticationAttempts(failedAuthenticationAttempts: Int)
}
class AuthenticationRepositoryImpl @Inject constructor() : AuthenticationRepository {
// TODO(b/280883900): get data from real data sources in SysUI.
class AuthenticationRepositoryImpl
@Inject
constructor(
private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val userRepository: UserRepository,
private val lockPatternUtils: LockPatternUtils,
) : AuthenticationRepository {
private val _isUnlocked = MutableStateFlow(false)
override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
private val _authenticationMethod =
MutableStateFlow<AuthenticationMethodModel>(
AuthenticationMethodModel.Pin(listOf(1, 2, 3, 4), autoConfirm = false)
)
override val authenticationMethod: StateFlow<AuthenticationMethodModel> =
_authenticationMethod.asStateFlow()
private val _isBypassEnabled = MutableStateFlow(false)
override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled.asStateFlow()
@@ -90,6 +93,41 @@ class AuthenticationRepositoryImpl @Inject constructor() : AuthenticationReposit
override val failedAuthenticationAttempts: StateFlow<Int> =
_failedAuthenticationAttempts.asStateFlow()
override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
return withContext(backgroundDispatcher) {
val selectedUserId = userRepository.getSelectedUserInfo().id
when (getSecurityMode.apply(selectedUserId)) {
KeyguardSecurityModel.SecurityMode.PIN,
KeyguardSecurityModel.SecurityMode.SimPin ->
AuthenticationMethodModel.Pin(
code = listOf(1, 2, 3, 4), // TODO(b/280883900): remove this
autoConfirm = lockPatternUtils.isAutoPinConfirmEnabled(selectedUserId),
)
KeyguardSecurityModel.SecurityMode.Password,
KeyguardSecurityModel.SecurityMode.SimPuk ->
AuthenticationMethodModel.Password(
password = "password", // TODO(b/280883900): remove this
)
KeyguardSecurityModel.SecurityMode.Pattern ->
AuthenticationMethodModel.Pattern(
coordinates =
listOf(
AuthenticationMethodModel.Pattern.PatternCoordinate(2, 0),
AuthenticationMethodModel.Pattern.PatternCoordinate(2, 1),
AuthenticationMethodModel.Pattern.PatternCoordinate(2, 2),
AuthenticationMethodModel.Pattern.PatternCoordinate(1, 1),
AuthenticationMethodModel.Pattern.PatternCoordinate(0, 0),
AuthenticationMethodModel.Pattern.PatternCoordinate(0, 1),
AuthenticationMethodModel.Pattern.PatternCoordinate(0, 2),
), // TODO(b/280883900): remove this
)
KeyguardSecurityModel.SecurityMode.None -> AuthenticationMethodModel.None
KeyguardSecurityModel.SecurityMode.Invalid -> error("Invalid security mode!")
null -> error("Invalid security is null!")
}
}
}
override fun setUnlocked(isUnlocked: Boolean) {
_isUnlocked.value = isUnlocked
}
@@ -98,10 +136,6 @@ class AuthenticationRepositoryImpl @Inject constructor() : AuthenticationReposit
_isBypassEnabled.value = isBypassEnabled
}
override fun setAuthenticationMethod(authenticationMethod: AuthenticationMethodModel) {
_authenticationMethod.value = authenticationMethod
}
override fun setFailedAuthenticationAttempts(failedAuthenticationAttempts: Int) {
_failedAuthenticationAttempts.value = failedAuthenticationAttempts
}

View File

@@ -25,9 +25,8 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/** Hosts application business logic related to authentication. */
@SysUISingleton
@@ -37,12 +36,6 @@ constructor(
@Application applicationScope: CoroutineScope,
private val repository: AuthenticationRepository,
) {
/**
* The currently-configured authentication method. This determines how the authentication
* challenge is completed in order to unlock an otherwise locked device.
*/
val authenticationMethod: StateFlow<AuthenticationMethodModel> = repository.authenticationMethod
/**
* Whether the device is unlocked.
*
@@ -52,20 +45,18 @@ constructor(
* Note that this state has no real bearing on whether the lock screen is showing or dismissed.
*/
val isUnlocked: StateFlow<Boolean> =
combine(authenticationMethod, repository.isUnlocked) { authMethod, isUnlocked ->
isUnlockedWithAuthMethod(
isUnlocked = isUnlocked,
authMethod = authMethod,
)
repository.isUnlocked
.map { isUnlocked ->
if (getAuthenticationMethod() is AuthenticationMethodModel.None) {
true
} else {
isUnlocked
}
}
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
initialValue =
isUnlockedWithAuthMethod(
isUnlocked = repository.isUnlocked.value,
authMethod = repository.authenticationMethod.value,
)
initialValue = true,
)
/**
@@ -82,25 +73,20 @@ constructor(
*/
val failedAuthenticationAttempts: StateFlow<Int> = repository.failedAuthenticationAttempts
init {
// UNLOCKS WHEN AUTH METHOD REMOVED.
//
// Unlocks the device if the auth method becomes None.
applicationScope.launch {
repository.authenticationMethod.collect {
if (it is AuthenticationMethodModel.None) {
unlockDevice()
}
}
}
/**
* Returns the currently-configured authentication method. This determines how the
* authentication challenge is completed in order to unlock an otherwise locked device.
*/
suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
return repository.getAuthenticationMethod()
}
/**
* Returns `true` if the device currently requires authentication before content can be viewed;
* `false` if content can be displayed without unlocking first.
*/
fun isAuthenticationRequired(): Boolean {
return !isUnlocked.value && authenticationMethod.value.isSecure
suspend fun isAuthenticationRequired(): Boolean {
return !isUnlocked.value && getAuthenticationMethod().isSecure
}
/**
@@ -133,8 +119,8 @@ constructor(
* @return `true` if the authentication succeeded and the device is now unlocked; `false` when
* authentication failed, `null` if the check was not performed.
*/
fun authenticate(input: List<Any>, tryAutoConfirm: Boolean = false): Boolean? {
val authMethod = this.authenticationMethod.value
suspend fun authenticate(input: List<Any>, tryAutoConfirm: Boolean = false): Boolean? {
val authMethod = getAuthenticationMethod()
if (tryAutoConfirm) {
if ((authMethod as? AuthenticationMethodModel.Pin)?.autoConfirm != true) {
// Do not attempt to authenticate unless the PIN lock is set to auto-confirm.
@@ -176,28 +162,12 @@ constructor(
repository.setUnlocked(true)
}
/** See [authenticationMethod]. */
fun setAuthenticationMethod(authenticationMethod: AuthenticationMethodModel) {
repository.setAuthenticationMethod(authenticationMethod)
}
/** See [isBypassEnabled]. */
fun toggleBypassEnabled() {
repository.setBypassEnabled(!repository.isBypassEnabled.value)
}
companion object {
private fun isUnlockedWithAuthMethod(
isUnlocked: Boolean,
authMethod: AuthenticationMethodModel,
): Boolean {
return if (authMethod is AuthenticationMethodModel.None) {
true
} else {
isUnlocked
}
}
/**
* Returns a PIN code from the given list. It's assumed the given list elements are all
* [Int] in the range [0-9].

View File

@@ -68,43 +68,39 @@ constructor(
)
)
/**
* The currently-configured authentication method. This determines how the authentication
* challenge is completed in order to unlock an otherwise locked device.
*/
val authenticationMethod: StateFlow<AuthenticationMethodModel> =
authenticationInteractor.authenticationMethod
/** The current authentication throttling state. If `null`, there's no throttling. */
val throttling: StateFlow<AuthenticationThrottledModel?> = repository.throttling
init {
applicationScope.launch {
combine(
sceneInteractor.currentScene(containerName),
authenticationInteractor.authenticationMethod,
::Pair,
)
.collect { (currentScene, authMethod) ->
if (currentScene.key == SceneKey.Bouncer) {
when (authMethod) {
is AuthenticationMethodModel.None ->
sceneInteractor.setCurrentScene(
containerName,
SceneModel(SceneKey.Gone),
)
is AuthenticationMethodModel.Swipe ->
sceneInteractor.setCurrentScene(
containerName,
SceneModel(SceneKey.Lockscreen),
)
else -> Unit
}
sceneInteractor.currentScene(containerName).collect { currentScene ->
if (currentScene.key == SceneKey.Bouncer) {
when (getAuthenticationMethod()) {
is AuthenticationMethodModel.None ->
sceneInteractor.setCurrentScene(
containerName,
SceneModel(SceneKey.Gone),
)
is AuthenticationMethodModel.Swipe ->
sceneInteractor.setCurrentScene(
containerName,
SceneModel(SceneKey.Lockscreen),
)
else -> Unit
}
}
}
}
}
/**
* Returns the currently-configured authentication method. This determines how the
* authentication challenge is completed in order to unlock an otherwise locked device.
*/
suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
return authenticationInteractor.getAuthenticationMethod()
}
/**
* Either shows the bouncer or unlocks the device, if the bouncer doesn't need to be shown.
*
@@ -115,18 +111,20 @@ constructor(
containerName: String,
message: String? = null,
) {
if (authenticationInteractor.isAuthenticationRequired()) {
repository.setMessage(message ?: promptMessage(authenticationMethod.value))
sceneInteractor.setCurrentScene(
containerName = containerName,
scene = SceneModel(SceneKey.Bouncer),
)
} else {
authenticationInteractor.unlockDevice()
sceneInteractor.setCurrentScene(
containerName = containerName,
scene = SceneModel(SceneKey.Gone),
)
applicationScope.launch {
if (authenticationInteractor.isAuthenticationRequired()) {
repository.setMessage(message ?: promptMessage(getAuthenticationMethod()))
sceneInteractor.setCurrentScene(
containerName = containerName,
scene = SceneModel(SceneKey.Bouncer),
)
} else {
authenticationInteractor.unlockDevice()
sceneInteractor.setCurrentScene(
containerName = containerName,
scene = SceneModel(SceneKey.Gone),
)
}
}
}
@@ -135,7 +133,7 @@ constructor(
* method.
*/
fun resetMessage() {
repository.setMessage(promptMessage(authenticationMethod.value))
applicationScope.launch { repository.setMessage(promptMessage(getAuthenticationMethod())) }
}
/** Removes the user-facing message. */
@@ -160,7 +158,7 @@ constructor(
* @return `true` if the authentication succeeded and the device is now unlocked; `false` when
* authentication failed, `null` if the check was not performed.
*/
fun authenticate(
suspend fun authenticate(
input: List<Any>,
tryAutoConfirm: Boolean = false,
): Boolean? {
@@ -198,7 +196,7 @@ constructor(
repository.setThrottling(null)
clearMessage()
}
else -> repository.setMessage(errorMessage(authenticationMethod.value))
else -> repository.setMessage(errorMessage(getAuthenticationMethod()))
}
return isAuthenticated

View File

@@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -66,6 +67,7 @@ constructor(
private val password: PasswordBouncerViewModel by lazy {
PasswordBouncerViewModel(
applicationScope = applicationScope,
interactor = interactor,
isInputEnabled = isInputEnabled,
)
@@ -81,14 +83,25 @@ constructor(
}
/** View-model for the current UI, based on the current authentication method. */
val authMethod: StateFlow<AuthMethodBouncerViewModel?> =
interactor.authenticationMethod
.map { authMethod -> toViewModel(authMethod) }
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue = toViewModel(interactor.authenticationMethod.value),
)
val authMethod: StateFlow<AuthMethodBouncerViewModel?>
get() =
flow {
emit(null)
emit(interactor.getAuthenticationMethod())
}
.map { authMethod ->
when (authMethod) {
is AuthenticationMethodModel.Pin -> pin
is AuthenticationMethodModel.Password -> password
is AuthenticationMethodModel.Pattern -> pattern
else -> null
}
}
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue = null,
)
/** The user-facing message to show in the bouncer. */
val message: StateFlow<MessageViewModel> =
@@ -125,7 +138,7 @@ constructor(
interactor.throttling
.map { model ->
model?.let {
when (interactor.authenticationMethod.value) {
when (interactor.getAuthenticationMethod()) {
is AuthenticationMethodModel.Pin ->
R.string.kg_too_many_failed_pin_attempts_dialog_message
is AuthenticationMethodModel.Password ->
@@ -161,17 +174,6 @@ constructor(
_throttlingDialogMessage.value = null
}
private fun toViewModel(
authMethod: AuthenticationMethodModel,
): AuthMethodBouncerViewModel? {
return when (authMethod) {
is AuthenticationMethodModel.Pin -> pin
is AuthenticationMethodModel.Password -> password
is AuthenticationMethodModel.Pattern -> pattern
else -> null
}
}
private fun toMessageViewModel(
message: String?,
throttling: AuthenticationThrottledModel?,

View File

@@ -17,12 +17,15 @@
package com.android.systemui.bouncer.ui.viewmodel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the password bouncer UI. */
class PasswordBouncerViewModel(
private val applicationScope: CoroutineScope,
private val interactor: BouncerInteractor,
isInputEnabled: StateFlow<Boolean>,
) :
@@ -50,10 +53,13 @@ class PasswordBouncerViewModel(
/** Notifies that the user has pressed the key for attempting to authenticate the password. */
fun onAuthenticateKeyPressed() {
if (interactor.authenticate(password.value.toCharArray().toList()) != true) {
showFailureAnimation()
}
val password = _password.value.toCharArray().toList()
_password.value = ""
applicationScope.launch {
if (interactor.authenticate(password) != true) {
showFailureAnimation()
}
}
}
}

View File

@@ -29,13 +29,15 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the pattern bouncer UI. */
class PatternBouncerViewModel(
private val applicationContext: Context,
applicationScope: CoroutineScope,
private val applicationScope: CoroutineScope,
private val interactor: BouncerInteractor,
isInputEnabled: StateFlow<Boolean>,
) :
@@ -69,12 +71,17 @@ class PatternBouncerViewModel(
/** Whether the pattern itself should be rendered visibly. */
val isPatternVisible: StateFlow<Boolean> =
interactor.authenticationMethod
.map { authMethod -> isPatternVisible(authMethod) }
flow {
emit(null)
emit(interactor.getAuthenticationMethod())
}
.map { authMethod ->
(authMethod as? AuthenticationMethodModel.Pattern)?.isPatternVisible ?: false
}
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
initialValue = isPatternVisible(interactor.authenticationMethod.value),
initialValue = false,
)
/** Notifies that the UI has been shown to the user. */
@@ -154,17 +161,15 @@ class PatternBouncerViewModel(
/** Notifies that the user has ended the drag gesture across the dot grid. */
fun onDragEnd() {
val pattern = _selectedDots.value.map { it.toCoordinate() }
if (interactor.authenticate(pattern) != true) {
showFailureAnimation()
}
_dots.value = defaultDots()
_currentDot.value = null
_selectedDots.value = linkedSetOf()
}
private fun isPatternVisible(authMethodModel: AuthenticationMethodModel): Boolean {
return (authMethodModel as? AuthenticationMethodModel.Pattern)?.isPatternVisible ?: false
applicationScope.launch {
if (interactor.authenticate(pattern) != true) {
showFailureAnimation()
}
}
}
private fun defaultDots(): List<PatternDotViewModel> {

View File

@@ -22,13 +22,14 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the PIN code bouncer UI. */
class PinBouncerViewModel(
applicationScope: CoroutineScope,
private val applicationScope: CoroutineScope,
private val interactor: BouncerInteractor,
isInputEnabled: StateFlow<Boolean>,
) :
@@ -39,40 +40,48 @@ class PinBouncerViewModel(
private val mutablePinEntries = MutableStateFlow<List<EnteredKey>>(emptyList())
val pinEntries: StateFlow<List<EnteredKey>> = mutablePinEntries
/** The length of the hinted PIN, or null if pin length hint should not be shown. */
/** The length of the hinted PIN, or `null` if pin length hint should not be shown. */
val hintedPinLength: StateFlow<Int?> =
interactor.authenticationMethod
.map { authMethod -> computeHintedPinLength(authMethod) }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
initialValue = computeHintedPinLength(interactor.authenticationMethod.value),
)
/** Appearance of the backspace button. */
val backspaceButtonAppearance: StateFlow<ActionButtonAppearance> =
combine(interactor.authenticationMethod, mutablePinEntries) { authMethod, enteredPin ->
computeBackspaceButtonAppearance(authMethod, enteredPin)
flow {
emit(null)
emit(interactor.getAuthenticationMethod())
}
.map { authMethod ->
// Hinting is enabled for 6-digit codes only
autoConfirmPinLength(authMethod).takeIf { it == HINTING_PASSCODE_LENGTH }
}
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
initialValue =
computeBackspaceButtonAppearance(
interactor.authenticationMethod.value,
mutablePinEntries.value
),
initialValue = null,
)
/** Appearance of the backspace button. */
val backspaceButtonAppearance: StateFlow<ActionButtonAppearance> =
mutablePinEntries
.map { mutablePinEntries ->
computeBackspaceButtonAppearance(
interactor.getAuthenticationMethod(),
mutablePinEntries
)
}
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
initialValue = ActionButtonAppearance.Hidden,
)
/** Appearance of the confirm button. */
val confirmButtonAppearance: StateFlow<ActionButtonAppearance> =
interactor.authenticationMethod
flow {
emit(null)
emit(interactor.getAuthenticationMethod())
}
.map { authMethod -> computeConfirmButtonAppearance(authMethod) }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
initialValue =
computeConfirmButtonAppearance(interactor.authenticationMethod.value),
initialValue = ActionButtonAppearance.Hidden,
)
/** Notifies that the UI has been shown to the user. */
@@ -111,30 +120,28 @@ class PinBouncerViewModel(
private fun tryAuthenticate(useAutoConfirm: Boolean) {
val pinCode = mutablePinEntries.value.map { it.input }
val isSuccess = interactor.authenticate(pinCode, useAutoConfirm) ?: return
if (!isSuccess) {
showFailureAnimation()
applicationScope.launch {
val isSuccess = interactor.authenticate(pinCode, useAutoConfirm) ?: return@launch
if (!isSuccess) {
showFailureAnimation()
}
mutablePinEntries.value = emptyList()
}
mutablePinEntries.value = emptyList()
}
private fun isAutoConfirmEnabled(authMethodModel: AuthenticationMethodModel): Boolean {
private fun isAutoConfirmEnabled(authMethodModel: AuthenticationMethodModel?): Boolean {
return (authMethodModel as? AuthenticationMethodModel.Pin)?.autoConfirm == true
}
private fun autoConfirmPinLength(authMethodModel: AuthenticationMethodModel): Int? {
private fun autoConfirmPinLength(authMethodModel: AuthenticationMethodModel?): Int? {
if (!isAutoConfirmEnabled(authMethodModel)) return null
return (authMethodModel as? AuthenticationMethodModel.Pin)?.code?.size
}
private fun computeHintedPinLength(authMethodModel: AuthenticationMethodModel): Int? {
// Hinting is enabled for 6-digit codes only
return autoConfirmPinLength(authMethodModel).takeIf { it == HINTING_PASSCODE_LENGTH }
}
private fun computeBackspaceButtonAppearance(
authMethodModel: AuthenticationMethodModel,
enteredPin: List<EnteredKey>
@@ -149,7 +156,7 @@ class PinBouncerViewModel(
}
}
private fun computeConfirmButtonAppearance(
authMethodModel: AuthenticationMethodModel
authMethodModel: AuthenticationMethodModel?
): ActionButtonAppearance {
return if (isAutoConfirmEnabled(authMethodModel)) {
ActionButtonAppearance.Hidden

View File

@@ -61,23 +61,16 @@ constructor(
/** Whether it's currently possible to swipe up to dismiss the lockscreen. */
val isSwipeToDismissEnabled: StateFlow<Boolean> =
combine(
authenticationInteractor.isUnlocked,
authenticationInteractor.authenticationMethod,
) { isUnlocked, authMethod ->
isSwipeToUnlockEnabled(
isUnlocked = isUnlocked,
authMethod = authMethod,
)
authenticationInteractor.isUnlocked
.map { isUnlocked ->
!isUnlocked &&
authenticationInteractor.getAuthenticationMethod() is
AuthenticationMethodModel.Swipe
}
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue =
isSwipeToUnlockEnabled(
isUnlocked = authenticationInteractor.isUnlocked.value,
authMethod = authenticationInteractor.authenticationMethod.value,
),
initialValue = false,
)
init {
@@ -124,44 +117,17 @@ constructor(
// If switched from the lockscreen to the gone scene and the auth method was a swipe,
// unlocks the device.
applicationScope.launch {
combine(
authenticationInteractor.authenticationMethod,
sceneInteractor.currentScene(containerName).pairwise(),
::Pair,
)
.collect { (authMethod, scenes) ->
val (previousScene, currentScene) = scenes
if (
authMethod is AuthenticationMethodModel.Swipe &&
previousScene.key == SceneKey.Lockscreen &&
currentScene.key == SceneKey.Gone
) {
authenticationInteractor.unlockDevice()
}
}
}
// DISMISS Lockscreen IF AUTH METHOD IS REMOVED.
//
// If the auth method becomes None while on the lockscreen scene, dismisses the lock
// screen.
applicationScope.launch {
combine(
authenticationInteractor.authenticationMethod,
sceneInteractor.currentScene(containerName),
::Pair,
)
.collect { (authMethod, scene) ->
if (
scene.key == SceneKey.Lockscreen &&
authMethod == AuthenticationMethodModel.None
) {
sceneInteractor.setCurrentScene(
containerName = containerName,
scene = SceneModel(SceneKey.Gone),
)
}
sceneInteractor.currentScene(containerName).pairwise().collect {
(previousScene, currentScene) ->
if (
authenticationInteractor.getAuthenticationMethod() is
AuthenticationMethodModel.Swipe &&
previousScene.key == SceneKey.Lockscreen &&
currentScene.key == SceneKey.Gone
) {
authenticationInteractor.unlockDevice()
}
}
}
}
@@ -170,13 +136,6 @@ constructor(
bouncerInteractor.showOrUnlockDevice(containerName = containerName)
}
private fun isSwipeToUnlockEnabled(
isUnlocked: Boolean,
authMethod: AuthenticationMethodModel,
): Boolean {
return !isUnlocked && authMethod is AuthenticationMethodModel.Swipe
}
@AssistedFactory
interface Factory {
fun create(

View File

@@ -25,7 +25,6 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -37,8 +36,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class AuthenticationInteractorTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val repository: AuthenticationRepository = utils.authenticationRepository()
private val underTest =
utils.authenticationInteractor(
@@ -46,23 +45,23 @@ class AuthenticationInteractorTest : SysuiTestCase() {
)
@Test
fun authMethod() =
fun getAuthenticationMethod() =
testScope.runTest {
val authMethod by collectLastValue(underTest.authenticationMethod)
assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin(1234))
assertThat(underTest.getAuthenticationMethod())
.isEqualTo(AuthenticationMethodModel.Pin(1234))
underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password"))
assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Password("password"))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
assertThat(underTest.getAuthenticationMethod())
.isEqualTo(AuthenticationMethodModel.Password("password"))
}
@Test
fun isUnlocked_whenAuthMethodIsNone_isTrue() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
val isUnlocked by collectLastValue(underTest.isUnlocked)
assertThat(isUnlocked).isFalse()
underTest.setAuthenticationMethod(AuthenticationMethodModel.None)
assertThat(isUnlocked).isTrue()
}
@@ -108,7 +107,9 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
underTest.lockDevice()
runCurrent()
underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password"))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
assertThat(underTest.isAuthenticationRequired()).isTrue()
}
@@ -118,7 +119,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
underTest.lockDevice()
runCurrent()
underTest.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(underTest.isAuthenticationRequired()).isFalse()
}
@@ -128,7 +129,9 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
underTest.unlockDevice()
runCurrent()
underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password"))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
assertThat(underTest.isAuthenticationRequired()).isFalse()
}
@@ -138,7 +141,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
underTest.unlockDevice()
runCurrent()
underTest.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(underTest.isAuthenticationRequired()).isFalse()
}
@@ -148,7 +151,9 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue()
@@ -161,7 +166,9 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse()
@@ -174,7 +181,9 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(listOf())).isFalse()
@@ -187,7 +196,9 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(9999999999999999))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(9999999999999999)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(List(16) { 9 })).isTrue()
@@ -206,7 +217,9 @@ class AuthenticationInteractorTest : SysuiTestCase() {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(99999999999999999))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(99999999999999999)
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate(List(17) { 9 })).isFalse()
@@ -219,7 +232,9 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password"))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate("password".toList())).isTrue()
@@ -232,7 +247,9 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password"))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate("alohomora".toList())).isFalse()
@@ -245,7 +262,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(
listOf(
AuthenticationMethodModel.Pattern.PatternCoordinate(
@@ -293,7 +310,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(
listOf(
AuthenticationMethodModel.Pattern.PatternCoordinate(
@@ -341,7 +358,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
assertThat(isUnlocked).isFalse()
@@ -356,7 +373,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
assertThat(isUnlocked).isFalse()
@@ -371,7 +388,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
assertThat(isUnlocked).isFalse()
@@ -386,7 +403,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
assertThat(isUnlocked).isFalse()
@@ -402,7 +419,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
assertThat(isUnlocked).isFalse()
@@ -417,7 +434,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = false)
)
assertThat(isUnlocked).isFalse()
@@ -432,22 +449,13 @@ class AuthenticationInteractorTest : SysuiTestCase() {
testScope.runTest {
val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts)
val isUnlocked by collectLastValue(underTest.isUnlocked)
underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password"))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
assertThat(isUnlocked).isFalse()
assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)).isNull()
assertThat(isUnlocked).isFalse()
assertThat(failedAttemptCount).isEqualTo(0)
}
@Test
fun unlocksDevice_whenAuthMethodBecomesNone() =
testScope.runTest {
val isUnlocked by collectLastValue(underTest.isUnlocked)
assertThat(isUnlocked).isFalse()
repository.setAuthenticationMethod(AuthenticationMethodModel.None)
assertThat(isUnlocked).isTrue()
}
}

View File

@@ -26,7 +26,6 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -40,8 +39,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class BouncerInteractorTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val authenticationInteractor =
utils.authenticationInteractor(
repository = utils.authenticationRepository(),
@@ -69,7 +68,9 @@ class BouncerInteractorTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
val message by collectLastValue(underTest.message)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
underTest.showOrUnlockDevice("container1")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -100,7 +101,7 @@ class BouncerInteractorTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
val message by collectLastValue(underTest.message)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
authenticationInteractor.lockDevice()
@@ -130,7 +131,7 @@ class BouncerInteractorTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
val message by collectLastValue(underTest.message)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = false)
)
authenticationInteractor.lockDevice()
@@ -154,7 +155,7 @@ class BouncerInteractorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
val message by collectLastValue(underTest.message)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
@@ -186,7 +187,7 @@ class BouncerInteractorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
val message by collectLastValue(underTest.message)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(emptyList())
)
authenticationInteractor.lockDevice()
@@ -222,7 +223,9 @@ class BouncerInteractorTest : SysuiTestCase() {
fun showOrUnlockDevice_notLocked_switchesToGoneScene() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
runCurrent()
@@ -235,7 +238,7 @@ class BouncerInteractorTest : SysuiTestCase() {
fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
authenticationInteractor.lockDevice()
underTest.showOrUnlockDevice("container1")
@@ -248,7 +251,7 @@ class BouncerInteractorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene("container1"))
val message by collectLastValue(underTest.message)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
@@ -266,7 +269,9 @@ class BouncerInteractorTest : SysuiTestCase() {
val throttling by collectLastValue(underTest.throttling)
val message by collectLastValue(underTest.message)
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(throttling).isNull()
assertThat(message).isEqualTo("")
assertThat(isUnlocked).isFalse()

View File

@@ -24,7 +24,6 @@ import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,8 +34,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class AuthMethodBouncerViewModelTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val authenticationInteractor =
utils.authenticationInteractor(
utils.authenticationRepository(),
@@ -55,7 +54,9 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() {
@Test
fun animateFailure() =
testScope.runTest {
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
val animateFailure by collectLastValue(underTest.animateFailure)
assertThat(animateFailure).isFalse()

View File

@@ -26,7 +26,6 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -38,8 +37,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class BouncerViewModelTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val authenticationInteractor =
utils.authenticationInteractor(
repository = utils.authenticationRepository(),
@@ -54,11 +53,11 @@ class BouncerViewModelTest : SysuiTestCase() {
@Test
fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() =
testScope.runTest {
val authMethodViewModel: AuthMethodBouncerViewModel? by
collectLastValue(underTest.authMethod)
authMethodsToTest().forEach { authMethod ->
authenticationInteractor.setAuthenticationMethod(authMethod)
utils.authenticationRepository.setAuthenticationMethod(authMethod)
val authMethodViewModel: AuthMethodBouncerViewModel? by
collectLastValue(underTest.authMethod)
if (authMethod.isSecure) {
assertThat(authMethodViewModel).isNotNull()
} else {
@@ -75,13 +74,13 @@ class BouncerViewModelTest : SysuiTestCase() {
collectLastValue(underTest.authMethod)
// First pass, populate our "seen" map:
authMethodsToTest().forEach { authMethod ->
authenticationInteractor.setAuthenticationMethod(authMethod)
utils.authenticationRepository.setAuthenticationMethod(authMethod)
authMethodViewModel?.let { seen[authMethod] = it }
}
// Second pass, assert same instances are reused:
authMethodsToTest().forEach { authMethod ->
authenticationInteractor.setAuthenticationMethod(authMethod)
utils.authenticationRepository.setAuthenticationMethod(authMethod)
authMethodViewModel?.let { assertThat(it).isSameInstanceAs(seen[authMethod]) }
}
}
@@ -97,7 +96,9 @@ class BouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val message by collectLastValue(underTest.message)
val throttling by collectLastValue(bouncerInteractor.throttling)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(message?.isUpdateAnimated).isTrue()
repeat(BouncerInteractor.THROTTLE_EVERY) {
@@ -120,7 +121,9 @@ class BouncerViewModelTest : SysuiTestCase() {
}
)
val throttling by collectLastValue(bouncerInteractor.throttling)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(isInputEnabled).isTrue()
repeat(BouncerInteractor.THROTTLE_EVERY) {
@@ -137,7 +140,9 @@ class BouncerViewModelTest : SysuiTestCase() {
fun throttlingDialogMessage() =
testScope.runTest {
val throttlingDialogMessage by collectLastValue(underTest.throttlingDialogMessage)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
repeat(BouncerInteractor.THROTTLE_EVERY) {
// Wrong PIN.

View File

@@ -28,7 +28,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -40,8 +40,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class PasswordBouncerViewModelTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val authenticationInteractor =
utils.authenticationInteractor(
repository = utils.authenticationRepository(),
@@ -58,6 +58,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
)
private val underTest =
PasswordBouncerViewModel(
applicationScope = testScope.backgroundScope,
interactor = bouncerInteractor,
isInputEnabled = MutableStateFlow(true).asStateFlow(),
)
@@ -75,7 +76,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
@@ -98,7 +99,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
@@ -106,6 +107,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
underTest.onPasswordInputChanged("password")
@@ -120,7 +122,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
@@ -143,7 +145,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
@@ -168,7 +170,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()

View File

@@ -29,7 +29,7 @@ import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -41,8 +41,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class PatternBouncerViewModelTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val authenticationInteractor =
utils.authenticationInteractor(
repository = utils.authenticationRepository(),
@@ -79,7 +79,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(CORRECT_PATTERN)
)
authenticationInteractor.lockDevice()
@@ -104,7 +104,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(CORRECT_PATTERN)
)
authenticationInteractor.lockDevice()
@@ -112,6 +112,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
underTest.onDragStart()
@@ -129,7 +130,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(CORRECT_PATTERN)
)
authenticationInteractor.lockDevice()
@@ -180,7 +181,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(CORRECT_PATTERN)
)
authenticationInteractor.lockDevice()
@@ -215,7 +216,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern(CORRECT_PATTERN)
)
authenticationInteractor.lockDevice()

View File

@@ -29,7 +29,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -41,8 +41,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class PinBouncerViewModelTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val authenticationInteractor =
utils.authenticationInteractor(
@@ -85,7 +85,6 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -106,12 +105,15 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
underTest.onPinButtonClicked(1)
@@ -129,12 +131,15 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
underTest.onPinButtonClicked(1)
assertThat(entries).hasSize(1)
@@ -153,7 +158,9 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -180,12 +187,15 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
runCurrent()
underTest.onPinButtonClicked(1)
underTest.onPinButtonClicked(2)
underTest.onPinButtonClicked(3)
@@ -204,7 +214,9 @@ class PinBouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -228,7 +240,9 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -255,7 +269,9 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer))
assertThat(isUnlocked).isFalse()
@@ -290,7 +306,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
authenticationInteractor.lockDevice()
@@ -314,7 +330,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME))
val message by collectLastValue(bouncerViewModel.message)
val entries by collectLastValue(underTest.pinEntries)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
authenticationInteractor.lockDevice()
@@ -338,7 +354,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = false)
)
@@ -349,7 +365,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
fun backspaceButtonAppearance_withAutoConfirmButNoInput_isHidden() =
testScope.runTest {
val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
@@ -360,7 +376,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
fun backspaceButtonAppearance_withAutoConfirmAndInput_isShownQuiet() =
testScope.runTest {
val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
@@ -374,7 +390,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
testScope.runTest {
val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = false)
)
@@ -385,7 +401,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
fun confirmButtonAppearance_withAutoConfirm_isHidden() =
testScope.runTest {
val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = true)
)
@@ -396,7 +412,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
fun hintedPinLength_withoutAutoConfirm_isNull() =
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234, autoConfirm = false)
)
@@ -407,7 +423,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
fun hintedPinLength_withAutoConfirmPinLessThanSixDigits_isNull() =
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(12345, autoConfirm = true)
)
@@ -418,7 +434,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
fun hintedPinLength_withAutoConfirmPinExactlySixDigits_isSix() =
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(123456, autoConfirm = true)
)
@@ -429,7 +445,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
fun hintedPinLength_withAutoConfirmPinMoreThanSixDigits_isNull() =
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234567, autoConfirm = true)
)

View File

@@ -26,7 +26,6 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -38,8 +37,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class LockscreenSceneInteractorTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val authenticationInteractor =
utils.authenticationInteractor(
@@ -74,7 +73,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
val isSwipeToDismissEnabled by collectLastValue(underTest.isSwipeToDismissEnabled)
authenticationInteractor.lockDevice()
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(isSwipeToDismissEnabled).isTrue()
}
@@ -85,7 +84,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
val isSwipeToDismissEnabled by collectLastValue(underTest.isSwipeToDismissEnabled)
authenticationInteractor.unlockDevice()
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(isSwipeToDismissEnabled).isFalse()
}
@@ -95,7 +94,9 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.lockDevice()
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
underTest.dismissLockscreen()
@@ -108,7 +109,9 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.unlockDevice()
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
underTest.dismissLockscreen()
@@ -121,7 +124,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.lockDevice()
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
underTest.dismissLockscreen()
@@ -182,7 +185,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Lockscreen))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(isUnlocked).isFalse()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone))
@@ -195,7 +198,9 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Lockscreen))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
assertThat(isUnlocked).isFalse()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone))
@@ -210,7 +215,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
runCurrent()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Shade))
runCurrent()
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
runCurrent()
assertThat(isUnlocked).isFalse()
@@ -219,30 +224,17 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
assertThat(isUnlocked).isFalse()
}
@Test
fun authMethodChangedToNone_onLockScreenScene_dismissesLockScreen() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Lockscreen))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.None)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun authMethodChangedToNone_notOnLockScreenScene_doesNotDismissLockScreen() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
runCurrent()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.QuickSettings))
runCurrent()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.QuickSettings))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.None)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.QuickSettings))
}

View File

@@ -29,7 +29,6 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -41,8 +40,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class LockscreenSceneViewModelTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val authenticationInteractor =
utils.authenticationInteractor(
@@ -73,7 +72,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun lockButtonIcon_whenLocked() =
testScope.runTest {
val lockButtonIcon by collectLastValue(underTest.lockButtonIcon)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.lockDevice()
@@ -86,7 +85,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun lockButtonIcon_whenUnlocked() =
testScope.runTest {
val lockButtonIcon by collectLastValue(underTest.lockButtonIcon)
authenticationInteractor.setAuthenticationMethod(
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password("password")
)
authenticationInteractor.unlockDevice()
@@ -99,7 +98,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun upTransitionSceneKey_swipeToUnlockedEnabled_gone() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe)
authenticationInteractor.lockDevice()
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
@@ -109,7 +108,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun upTransitionSceneKey_swipeToUnlockedNotEnabled_bouncer() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
@@ -119,7 +120,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun onLockButtonClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
runCurrent()
@@ -132,7 +135,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
runCurrent()
@@ -145,7 +150,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
runCurrent()
@@ -158,7 +165,9 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
fun onLockButtonClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
runCurrent()

View File

@@ -27,7 +27,6 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -39,8 +38,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class QuickSettingsSceneViewModelTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val authenticationInteractor =
utils.authenticationInteractor(
@@ -70,7 +69,9 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
runCurrent()
@@ -83,7 +84,9 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
runCurrent()

View File

@@ -27,7 +27,6 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -39,8 +38,8 @@ import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class ShadeSceneViewModelTest : SysuiTestCase() {
private val testScope = TestScope()
private val utils = SceneTestUtils(this, testScope)
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val authenticationInteractor =
utils.authenticationInteractor(
@@ -71,7 +70,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
fun upTransitionSceneKey_deviceLocked_lockScreen() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen)
@@ -81,7 +82,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
fun upTransitionSceneKey_deviceUnlocked_gone() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
@@ -91,7 +94,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.unlockDevice()
runCurrent()
@@ -104,7 +109,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234))
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin(1234)
)
authenticationInteractor.lockDevice()
runCurrent()

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2023 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.authentication.data.repository
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
class FakeAuthenticationRepository(
private val delegate: AuthenticationRepository,
private val onSecurityModeChanged: (SecurityMode) -> Unit,
) : AuthenticationRepository by delegate {
private var authenticationMethod: AuthenticationMethodModel = DEFAULT_AUTHENTICATION_METHOD
override suspend fun getAuthenticationMethod(): AuthenticationMethodModel {
return authenticationMethod
}
fun setAuthenticationMethod(authenticationMethod: AuthenticationMethodModel) {
this.authenticationMethod = authenticationMethod
onSecurityModeChanged(authenticationMethod.toSecurityMode())
}
companion object {
val DEFAULT_AUTHENTICATION_METHOD =
AuthenticationMethodModel.Pin(listOf(1, 2, 3, 4), autoConfirm = false)
fun AuthenticationMethodModel.toSecurityMode(): SecurityMode {
return when (this) {
is AuthenticationMethodModel.Pin -> SecurityMode.PIN
is AuthenticationMethodModel.Password -> SecurityMode.Password
is AuthenticationMethodModel.Pattern -> SecurityMode.Pattern
is AuthenticationMethodModel.Swipe,
is AuthenticationMethodModel.None -> SecurityMode.None
}
}
}
}

View File

@@ -16,9 +16,12 @@
package com.android.systemui.scene
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.AuthenticationRepository
import com.android.systemui.authentication.data.repository.AuthenticationRepositoryImpl
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository.Companion.toSecurityMode
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
@@ -28,8 +31,12 @@ import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
/**
@@ -39,9 +46,23 @@ import kotlinx.coroutines.test.TestScope
@OptIn(ExperimentalCoroutinesApi::class)
class SceneTestUtils(
test: SysuiTestCase,
private val testScope: TestScope? = null,
) {
val testDispatcher: TestDispatcher by lazy { StandardTestDispatcher() }
val testScope: TestScope by lazy { TestScope(testDispatcher) }
private var securityMode: SecurityMode =
FakeAuthenticationRepository.DEFAULT_AUTHENTICATION_METHOD.toSecurityMode()
val authenticationRepository: FakeAuthenticationRepository by lazy {
FakeAuthenticationRepository(
delegate =
AuthenticationRepositoryImpl(
getSecurityMode = { securityMode },
backgroundDispatcher = testDispatcher,
userRepository = FakeUserRepository(),
lockPatternUtils = mock(),
),
onSecurityModeChanged = { securityMode = it },
)
}
private val context = test.context
fun fakeSceneContainerRepository(
@@ -82,7 +103,7 @@ class SceneTestUtils(
}
fun authenticationRepository(): AuthenticationRepository {
return AuthenticationRepositoryImpl()
return authenticationRepository
}
fun authenticationInteractor(
@@ -94,17 +115,6 @@ class SceneTestUtils(
)
}
private fun applicationScope(): CoroutineScope {
return checkNotNull(testScope) {
"""
TestScope not initialized, please create a TestScope and inject it into
SceneTestUtils.
"""
.trimIndent()
}
.backgroundScope
}
fun bouncerInteractor(
authenticationInteractor: AuthenticationInteractor,
sceneInteractor: SceneInteractor,
@@ -154,6 +164,10 @@ class SceneTestUtils(
)
}
private fun applicationScope(): CoroutineScope {
return testScope.backgroundScope
}
companion object {
const val CONTAINER_1 = "container1"
const val CONTAINER_2 = "container2"