[flexiglass] Hooks up real app state and logic in keyguard (1)

- Source auth method from KeyguardSecurityModel, changing it from flow to getter

Bug: 280883900
Test: Unit tests still pass.
Test: manually verified that changing the screen lock setting in
Settings, between PIN, password, and pattern, properly affects
Flexiglass in System UI such that the right version of the bouncer UI is
displayed.

Change-Id: Id457458806dc6ac2720abe21a77d5e50b83836fc
This commit is contained in:
Alejandro Nijamkin
2023-06-21 15:18:18 -07:00
parent 990ed238af
commit ace6b8065f
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"