Add missing screenshot and improve UX when user enter wrong pin then correct pin.

This commit is contained in:
Benoit Marty 2026-05-06 14:48:55 +02:00 committed by Benoit Marty
parent ab48f761e1
commit f529f49e3c
2 changed files with 42 additions and 23 deletions

View file

@ -20,7 +20,7 @@ open class PinUnlockStateProvider : PreviewParameterProvider<PinUnlockState> {
override val values: Sequence<PinUnlockState> override val values: Sequence<PinUnlockState>
get() = sequenceOf( get() = sequenceOf(
aPinUnlockState(), aPinUnlockState(),
aPinUnlockState(pinEntry = PinEntry.createEmpty(4).fillWith("12")), aPinUnlockState(pinEntry = AsyncData.Success(PinEntry.createEmpty(4).fillWith("12"))),
aPinUnlockState(showWrongPinTitle = true), aPinUnlockState(showWrongPinTitle = true),
aPinUnlockState(showSignOutPrompt = true), aPinUnlockState(showSignOutPrompt = true),
aPinUnlockState(showBiometricUnlock = false), aPinUnlockState(showBiometricUnlock = false),
@ -31,11 +31,18 @@ open class PinUnlockStateProvider : PreviewParameterProvider<PinUnlockState> {
BiometricUnlockError(BiometricPrompt.ERROR_LOCKOUT, "Biometric auth disabled") BiometricUnlockError(BiometricPrompt.ERROR_LOCKOUT, "Biometric auth disabled")
) )
), ),
aPinUnlockState(showSignOutPrompt = true, pinEntry = AsyncData.Failure(Exception("An error occurred"))),
// User enter wrong pin once, and then correct PIN. In this case, the error (with counter reset to 3) should not be displayed.
aPinUnlockState(
remainingAttempts = AsyncData.Success(2),
showWrongPinTitle = true,
isUnlocked = true,
),
) )
} }
fun aPinUnlockState( fun aPinUnlockState(
pinEntry: PinEntry = PinEntry.createEmpty(4), pinEntry: AsyncData<PinEntry> = AsyncData.Success(PinEntry.createEmpty(4)),
remainingAttempts: AsyncData<Int> = AsyncData.Success(3), remainingAttempts: AsyncData<Int> = AsyncData.Success(3),
showWrongPinTitle: Boolean = false, showWrongPinTitle: Boolean = false,
showSignOutPrompt: Boolean = false, showSignOutPrompt: Boolean = false,
@ -44,7 +51,7 @@ fun aPinUnlockState(
isUnlocked: Boolean = false, isUnlocked: Boolean = false,
signOutAction: AsyncAction<Unit> = AsyncAction.Uninitialized, signOutAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
) = PinUnlockState( ) = PinUnlockState(
pinEntry = AsyncData.Success(pinEntry), pinEntry = pinEntry,
showWrongPinTitle = showWrongPinTitle, showWrongPinTitle = showWrongPinTitle,
remainingAttempts = remainingAttempts, remainingAttempts = remainingAttempts,
showSignOutPrompt = showSignOutPrompt, showSignOutPrompt = showSignOutPrompt,

View file

@ -108,10 +108,10 @@ private fun PinUnlockPage(
) { ) {
BoxWithConstraints { BoxWithConstraints {
val commonModifier = Modifier val commonModifier = Modifier
.fillMaxSize() .fillMaxSize()
.systemBarsPadding() .systemBarsPadding()
.imePadding() .imePadding()
.padding(all = 20.dp) .padding(all = 20.dp)
val header = @Composable { val header = @Composable {
PinUnlockHeader( PinUnlockHeader(
@ -147,8 +147,8 @@ private fun PinUnlockPage(
state.eventSink(PinUnlockEvent.OnPinEntryChanged(it)) state.eventSink(PinUnlockEvent.OnPinEntryChanged(it))
}, },
modifier = Modifier modifier = Modifier
.focusRequester(focusRequester) .focusRequester(focusRequester)
.fillMaxWidth() .fillMaxWidth()
) )
} }
} else { } else {
@ -217,8 +217,8 @@ private fun PinUnlockCompactView(
} }
BoxWithConstraints( BoxWithConstraints(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.fillMaxHeight(), .fillMaxHeight(),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
content() content()
@ -239,9 +239,9 @@ private fun PinUnlockExpandedView(
header() header()
BoxWithConstraints( BoxWithConstraints(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.fillMaxWidth() .fillMaxWidth()
.padding(top = 40.dp), .padding(top = 40.dp),
) { ) {
content() content()
} }
@ -274,8 +274,8 @@ private fun PinDot(
} }
Box( Box(
modifier = Modifier modifier = Modifier
.size(14.dp) .size(14.dp)
.background(backgroundColor, CircleShape) .background(backgroundColor, CircleShape)
) )
} }
@ -311,14 +311,26 @@ private fun PinUnlockHeader(
) )
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
val remainingAttempts = state.remainingAttempts.dataOrNull() val remainingAttempts = state.remainingAttempts.dataOrNull()
val subtitle = if (remainingAttempts != null) { val subtitle = when {
if (state.showWrongPinTitle) { state.isUnlocked -> {
pluralStringResource(id = R.plurals.screen_app_lock_subtitle_wrong_pin, count = remainingAttempts, remainingAttempts) // Hide any previous error
} else { ""
pluralStringResource(id = R.plurals.screen_app_lock_subtitle, count = remainingAttempts, remainingAttempts)
} }
} else { remainingAttempts != null ->
"" if (state.showWrongPinTitle) {
pluralStringResource(
id = R.plurals.screen_app_lock_subtitle_wrong_pin,
count = remainingAttempts,
remainingAttempts,
)
} else {
pluralStringResource(
id = R.plurals.screen_app_lock_subtitle,
count = remainingAttempts,
remainingAttempts,
)
}
else -> ""
} }
val subtitleColor = if (state.showWrongPinTitle) { val subtitleColor = if (state.showWrongPinTitle) {
ElementTheme.colors.textCriticalPrimary ElementTheme.colors.textCriticalPrimary