[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:
@@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user