mirror of
https://github.com/bitwarden/android.git
synced 2026-06-09 08:09:16 -05:00
BIT-1630: Add unlock with biometrics flow (#827)
This commit is contained in:
committed by
Álison Fernandes
parent
31d54b3dc2
commit
b199a67b7d
@@ -43,12 +43,15 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenFilledButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenLogoutConfirmationDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenOverflowActionItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
|
||||
import com.x8bit.bitwarden.ui.platform.components.OverflowMenuItemData
|
||||
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.LocalBiometricsManager
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@@ -60,6 +63,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
@Composable
|
||||
fun VaultUnlockScreen(
|
||||
viewModel: VaultUnlockViewModel = hiltViewModel(),
|
||||
biometricsManager: BiometricsManager = LocalBiometricsManager.current,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
val context = LocalContext.current
|
||||
@@ -113,6 +117,12 @@ fun VaultUnlockScreen(
|
||||
)
|
||||
}
|
||||
|
||||
val onBiometricsUnlockClick: () -> Unit = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultUnlockAction.BiometricsUnlockClick) }
|
||||
}
|
||||
val onBiometricsLockOut: () -> Unit = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultUnlockAction.BiometricsLockOut) }
|
||||
}
|
||||
// Content
|
||||
BitwardenScaffold(
|
||||
modifier = Modifier
|
||||
@@ -182,6 +192,27 @@ fun VaultUnlockScreen(
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
if (state.isBiometricEnabled) {
|
||||
BitwardenOutlinedButton(
|
||||
label = stringResource(id = R.string.use_biometrics_to_unlock),
|
||||
onClick = {
|
||||
biometricsManager.promptBiometrics(
|
||||
onSuccess = onBiometricsUnlockClick,
|
||||
onCancel = {
|
||||
// no-op
|
||||
},
|
||||
onError = {
|
||||
// no-op
|
||||
},
|
||||
onLockOut = onBiometricsLockOut,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(id = R.string.unlock),
|
||||
onClick = remember(viewModel) {
|
||||
|
||||
@@ -87,6 +87,8 @@ class VaultUnlockViewModel @Inject constructor(
|
||||
is VaultUnlockAction.LockAccountClick -> handleLockAccountClick(action)
|
||||
is VaultUnlockAction.LogoutAccountClick -> handleLogoutAccountClick(action)
|
||||
is VaultUnlockAction.SwitchAccountClick -> handleSwitchAccountClick(action)
|
||||
VaultUnlockAction.BiometricsLockOut -> handleBiometricsLockOut()
|
||||
VaultUnlockAction.BiometricsUnlockClick -> handleBiometricsUnlockClick()
|
||||
VaultUnlockAction.UnlockClick -> handleUnlockClick()
|
||||
is VaultUnlockAction.Internal -> handleInternalAction(action)
|
||||
}
|
||||
@@ -122,6 +124,26 @@ class VaultUnlockViewModel @Inject constructor(
|
||||
authRepository.switchAccount(userId = action.accountSummary.userId)
|
||||
}
|
||||
|
||||
private fun handleBiometricsLockOut() {
|
||||
// TODO: Handle biometrics lockout (BIT-1451)
|
||||
sendEvent(VaultUnlockEvent.ShowToast("Lock out not yet implemented".asText()))
|
||||
}
|
||||
|
||||
private fun handleBiometricsUnlockClick() {
|
||||
val activeUserId = authRepository.activeUserId ?: return
|
||||
mutableStateFlow.update { it.copy(dialog = VaultUnlockState.VaultUnlockDialog.Loading) }
|
||||
viewModelScope.launch {
|
||||
val vaultUnlockResult = vaultRepo.unlockVaultWithBiometrics()
|
||||
sendAction(
|
||||
VaultUnlockAction.Internal.ReceiveVaultUnlockResult(
|
||||
userId = activeUserId,
|
||||
vaultUnlockResult = vaultUnlockResult,
|
||||
isBiometricLogin = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUnlockClick() {
|
||||
val activeUserId = authRepository.activeUserId ?: return
|
||||
mutableStateFlow.update { it.copy(dialog = VaultUnlockState.VaultUnlockDialog.Loading) }
|
||||
@@ -143,6 +165,7 @@ class VaultUnlockViewModel @Inject constructor(
|
||||
VaultUnlockAction.Internal.ReceiveVaultUnlockResult(
|
||||
userId = activeUserId,
|
||||
vaultUnlockResult = vaultUnlockResult,
|
||||
isBiometricLogin = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -175,7 +198,11 @@ class VaultUnlockViewModel @Inject constructor(
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialog = VaultUnlockState.VaultUnlockDialog.Error(
|
||||
state.vaultUnlockType.unlockScreenErrorMessage,
|
||||
if (action.isBiometricLogin) {
|
||||
R.string.generic_error_message.asText()
|
||||
} else {
|
||||
state.vaultUnlockType.unlockScreenErrorMessage
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -327,6 +354,16 @@ sealed class VaultUnlockAction {
|
||||
val accountSummary: AccountSummary,
|
||||
) : VaultUnlockAction()
|
||||
|
||||
/**
|
||||
* The user has clicked the biometrics button.
|
||||
*/
|
||||
data object BiometricsUnlockClick : VaultUnlockAction()
|
||||
|
||||
/**
|
||||
* The user has attempted to login with biometrics too many times and has been locked out.
|
||||
*/
|
||||
data object BiometricsLockOut : VaultUnlockAction()
|
||||
|
||||
/**
|
||||
* The user has clicked the unlock button.
|
||||
*/
|
||||
@@ -342,6 +379,7 @@ sealed class VaultUnlockAction {
|
||||
data class ReceiveVaultUnlockResult(
|
||||
val userId: String,
|
||||
val vaultUnlockResult: VaultUnlockResult,
|
||||
val isBiometricLogin: Boolean,
|
||||
) : Internal()
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user