mirror of
https://github.com/bitwarden/android.git
synced 2025-02-17 04:19:54 +03:00
BIT-614: Add ability to setup biometrics from account security screen (#826)
This commit is contained in:
parent
cf7f0ad7fe
commit
8f21fb466e
2 changed files with 128 additions and 14 deletions
|
@ -8,6 +8,7 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.BiometricsKeyResult
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault
|
||||
|
@ -220,9 +221,21 @@ class AccountSecurityViewModel @Inject constructor(
|
|||
private fun handleUnlockWithBiometricToggle(
|
||||
action: AccountSecurityAction.UnlockWithBiometricToggle,
|
||||
) {
|
||||
// TODO Display alert
|
||||
mutableStateFlow.update { it.copy(isUnlockWithBiometricsEnabled = action.enabled) }
|
||||
sendEvent(AccountSecurityEvent.ShowToast("Handle unlock with biometrics.".asText()))
|
||||
if (action.enabled) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialog = AccountSecurityDialog.Loading(R.string.saving.asText()),
|
||||
isUnlockWithBiometricsEnabled = true,
|
||||
)
|
||||
}
|
||||
viewModelScope.launch {
|
||||
val result = settingsRepository.setupBiometricsKey()
|
||||
sendAction(AccountSecurityAction.Internal.BiometricsKeyResultReceive(result))
|
||||
}
|
||||
} else {
|
||||
settingsRepository.clearBiometricsKey()
|
||||
mutableStateFlow.update { it.copy(isUnlockWithBiometricsEnabled = false) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUnlockWithPinToggle(action: AccountSecurityAction.UnlockWithPinToggle) {
|
||||
|
@ -248,12 +261,40 @@ class AccountSecurityViewModel @Inject constructor(
|
|||
|
||||
private fun handleInternalAction(action: AccountSecurityAction.Internal) {
|
||||
when (action) {
|
||||
is AccountSecurityAction.Internal.BiometricsKeyResultReceive -> {
|
||||
handleBiometricsKeyResultReceive(action)
|
||||
}
|
||||
|
||||
is AccountSecurityAction.Internal.FingerprintResultReceive -> {
|
||||
handleFingerprintResultReceived(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleBiometricsKeyResultReceive(
|
||||
action: AccountSecurityAction.Internal.BiometricsKeyResultReceive,
|
||||
) {
|
||||
when (action.result) {
|
||||
BiometricsKeyResult.Error -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialog = null,
|
||||
isUnlockWithBiometricsEnabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
BiometricsKeyResult.Success -> {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialog = null,
|
||||
isUnlockWithBiometricsEnabled = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFingerprintResultReceived(
|
||||
action: AccountSecurityAction.Internal.FingerprintResultReceive,
|
||||
) {
|
||||
|
@ -515,6 +556,13 @@ sealed class AccountSecurityAction {
|
|||
* Models actions that can be sent by the view model itself.
|
||||
*/
|
||||
sealed class Internal : AccountSecurityAction() {
|
||||
/**
|
||||
* A biometrics key result has been received.
|
||||
*/
|
||||
data class BiometricsKeyResultReceive(
|
||||
val result: BiometricsKeyResult,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
* A fingerprint has been received.
|
||||
*/
|
||||
|
|
|
@ -2,10 +2,12 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
|
|||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult
|
||||
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.BiometricsKeyResult
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
||||
|
@ -259,20 +261,84 @@ class AccountSecurityViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `on UnlockWithBiometricToggle should emit ShowToast`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AccountSecurityAction.UnlockWithBiometricToggle(true))
|
||||
fun `on UnlockWithBiometricToggle false should call clearBiometricsKey and update the state`() =
|
||||
runTest {
|
||||
val initialState = DEFAULT_STATE.copy(isUnlockWithBiometricsEnabled = true)
|
||||
every { settingsRepository.isUnlockWithBiometricsEnabled } returns true
|
||||
every { settingsRepository.clearBiometricsKey() } just runs
|
||||
val viewModel = createViewModel(initialState)
|
||||
assertEquals(initialState, viewModel.stateFlow.value)
|
||||
|
||||
viewModel.trySendAction(AccountSecurityAction.UnlockWithBiometricToggle(false))
|
||||
|
||||
assertEquals(
|
||||
AccountSecurityEvent.ShowToast("Handle unlock with biometrics.".asText()),
|
||||
awaitItem(),
|
||||
initialState.copy(isUnlockWithBiometricsEnabled = false),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
verify(exactly = 1) {
|
||||
settingsRepository.clearBiometricsKey()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on UnlockWithBiometricToggle true and setupBiometricsKey error should call update the state accordingly`() =
|
||||
runTest {
|
||||
coEvery { settingsRepository.setupBiometricsKey() } returns BiometricsKeyResult.Error
|
||||
val viewModel = createViewModel()
|
||||
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(DEFAULT_STATE, awaitItem())
|
||||
viewModel.trySendAction(AccountSecurityAction.UnlockWithBiometricToggle(true))
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialog = AccountSecurityDialog.Loading(R.string.saving.asText()),
|
||||
isUnlockWithBiometricsEnabled = true,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialog = null,
|
||||
isUnlockWithBiometricsEnabled = false,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
coVerify(exactly = 1) {
|
||||
settingsRepository.setupBiometricsKey()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on UnlockWithBiometricToggle true and setupBiometricsKey success should call update the state accordingly`() =
|
||||
runTest {
|
||||
coEvery { settingsRepository.setupBiometricsKey() } returns BiometricsKeyResult.Success
|
||||
val viewModel = createViewModel()
|
||||
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(DEFAULT_STATE, awaitItem())
|
||||
viewModel.trySendAction(AccountSecurityAction.UnlockWithBiometricToggle(true))
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialog = AccountSecurityDialog.Loading(R.string.saving.asText()),
|
||||
isUnlockWithBiometricsEnabled = true,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialog = null,
|
||||
isUnlockWithBiometricsEnabled = true,
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
coVerify(exactly = 1) {
|
||||
settingsRepository.setupBiometricsKey()
|
||||
}
|
||||
}
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(isUnlockWithBiometricsEnabled = true),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
|
|
Loading…
Add table
Reference in a new issue