PinUnlockEvents -> PinUnlockEvent

This commit is contained in:
Benoit Marty 2026-05-06 10:41:02 +02:00 committed by Benoit Marty
parent b2676d2547
commit ef11ce1100
5 changed files with 40 additions and 40 deletions

View file

@ -10,12 +10,12 @@ package io.element.android.features.lockscreen.impl.unlock
import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel
sealed interface PinUnlockEvents { sealed interface PinUnlockEvent {
data class OnPinKeypadPressed(val pinKeypadModel: PinKeypadModel) : PinUnlockEvents data class OnPinKeypadPressed(val pinKeypadModel: PinKeypadModel) : PinUnlockEvent
data class OnPinEntryChanged(val entryAsText: String) : PinUnlockEvents data class OnPinEntryChanged(val entryAsText: String) : PinUnlockEvent
data object OnForgetPin : PinUnlockEvents data object OnForgetPin : PinUnlockEvent
data object ClearSignOutPrompt : PinUnlockEvents data object ClearSignOutPrompt : PinUnlockEvent
data object SignOut : PinUnlockEvents data object SignOut : PinUnlockEvent
data object OnUseBiometric : PinUnlockEvents data object OnUseBiometric : PinUnlockEvent
data object ClearBiometricError : PinUnlockEvents data object ClearBiometricError : PinUnlockEvent
} }

View file

@ -101,28 +101,28 @@ class PinUnlockPresenter(
isUnlocked.value = true isUnlocked.value = true
} }
fun handleEvent(event: PinUnlockEvents) { fun handleEvent(event: PinUnlockEvent) {
when (event) { when (event) {
is PinUnlockEvents.OnPinKeypadPressed -> { is PinUnlockEvent.OnPinKeypadPressed -> {
pinEntryState.value = pinEntry.process(event.pinKeypadModel) pinEntryState.value = pinEntry.process(event.pinKeypadModel)
} }
PinUnlockEvents.OnForgetPin -> showSignOutPrompt = true PinUnlockEvent.OnForgetPin -> showSignOutPrompt = true
PinUnlockEvents.ClearSignOutPrompt -> showSignOutPrompt = false PinUnlockEvent.ClearSignOutPrompt -> showSignOutPrompt = false
PinUnlockEvents.SignOut -> { PinUnlockEvent.SignOut -> {
if (showSignOutPrompt) { if (showSignOutPrompt) {
showSignOutPrompt = false showSignOutPrompt = false
coroutineScope.signOut(signOutAction) coroutineScope.signOut(signOutAction)
} }
} }
PinUnlockEvents.OnUseBiometric -> { PinUnlockEvent.OnUseBiometric -> {
coroutineScope.launch { coroutineScope.launch {
biometricUnlockResult = biometricUnlock.authenticate() biometricUnlockResult = biometricUnlock.authenticate()
} }
} }
PinUnlockEvents.ClearBiometricError -> { PinUnlockEvent.ClearBiometricError -> {
biometricUnlockResult = null biometricUnlockResult = null
} }
is PinUnlockEvents.OnPinEntryChanged -> { is PinUnlockEvent.OnPinEntryChanged -> {
pinEntryState.value = pinEntry.process(event.entryAsText) pinEntryState.value = pinEntry.process(event.entryAsText)
} }
} }

View file

@ -23,7 +23,7 @@ data class PinUnlockState(
val showBiometricUnlock: Boolean, val showBiometricUnlock: Boolean,
val isUnlocked: Boolean, val isUnlocked: Boolean,
val biometricUnlockResult: BiometricAuthenticator.AuthenticationResult?, val biometricUnlockResult: BiometricAuthenticator.AuthenticationResult?,
val eventSink: (PinUnlockEvents) -> Unit val eventSink: (PinUnlockEvent) -> Unit
) { ) {
val isSignOutPromptCancellable = if (pinEntry.isFailure()) { val isSignOutPromptCancellable = if (pinEntry.isFailure()) {
false false

View file

@ -69,7 +69,7 @@ fun PinUnlockView(
) { ) {
OnLifecycleEvent { _, event -> OnLifecycleEvent { _, event ->
when (event) { when (event) {
Lifecycle.Event.ON_RESUME -> state.eventSink.invoke(PinUnlockEvents.OnUseBiometric) Lifecycle.Event.ON_RESUME -> state.eventSink.invoke(PinUnlockEvent.OnUseBiometric)
else -> Unit else -> Unit
} }
} }
@ -78,8 +78,8 @@ fun PinUnlockView(
if (state.showSignOutPrompt) { if (state.showSignOutPrompt) {
SignOutPrompt( SignOutPrompt(
isCancellable = state.isSignOutPromptCancellable, isCancellable = state.isSignOutPromptCancellable,
onSignOut = { state.eventSink(PinUnlockEvents.SignOut) }, onSignOut = { state.eventSink(PinUnlockEvent.SignOut) },
onDismiss = { state.eventSink(PinUnlockEvents.ClearSignOutPrompt) }, onDismiss = { state.eventSink(PinUnlockEvent.ClearSignOutPrompt) },
) )
} }
when (state.signOutAction) { when (state.signOutAction) {
@ -95,7 +95,7 @@ fun PinUnlockView(
if (state.showBiometricUnlockError) { if (state.showBiometricUnlockError) {
ErrorDialog( ErrorDialog(
content = state.biometricUnlockErrorMessage ?: "", content = state.biometricUnlockErrorMessage ?: "",
onSubmit = { state.eventSink(PinUnlockEvents.ClearBiometricError) } onSubmit = { state.eventSink(PinUnlockEvent.ClearBiometricError) }
) )
} }
} }
@ -125,10 +125,10 @@ private fun PinUnlockPage(
modifier = Modifier.padding(top = 24.dp), modifier = Modifier.padding(top = 24.dp),
showBiometricUnlock = state.showBiometricUnlock, showBiometricUnlock = state.showBiometricUnlock,
onUseBiometric = { onUseBiometric = {
state.eventSink(PinUnlockEvents.OnUseBiometric) state.eventSink(PinUnlockEvent.OnUseBiometric)
}, },
onForgotPin = { onForgotPin = {
state.eventSink(PinUnlockEvents.OnForgetPin) state.eventSink(PinUnlockEvent.OnForgetPin)
}, },
) )
} }
@ -144,7 +144,7 @@ private fun PinUnlockPage(
pinEntry = pinEntry, pinEntry = pinEntry,
isSecured = true, isSecured = true,
onValueChange = { onValueChange = {
state.eventSink(PinUnlockEvents.OnPinEntryChanged(it)) state.eventSink(PinUnlockEvent.OnPinEntryChanged(it))
}, },
modifier = Modifier modifier = Modifier
.focusRequester(focusRequester) .focusRequester(focusRequester)
@ -154,7 +154,7 @@ private fun PinUnlockPage(
} else { } else {
PinKeypad( PinKeypad(
onClick = { onClick = {
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(it)) state.eventSink(PinUnlockEvent.OnPinKeypadPressed(it))
}, },
maxWidth = constraints.maxWidth, maxWidth = constraints.maxWidth,
maxHeight = constraints.maxHeight, maxHeight = constraints.maxHeight,

View file

@ -46,17 +46,17 @@ class PinUnlockPresenterTest {
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.pinEntry).isInstanceOf(AsyncData.Success::class.java) assertThat(state.pinEntry).isInstanceOf(AsyncData.Success::class.java)
assertThat(state.remainingAttempts).isInstanceOf(AsyncData.Success::class.java) assertThat(state.remainingAttempts).isInstanceOf(AsyncData.Success::class.java)
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('1'))) state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('1')))
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('2'))) state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('2')))
} }
skipItems(1) skipItems(1)
awaitItem().also { state -> awaitItem().also { state ->
state.pinEntry.assertText(halfCompletePin) state.pinEntry.assertText(halfCompletePin)
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('3'))) state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('3')))
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Back)) state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Back))
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Empty)) state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Empty))
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('3'))) state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('3')))
state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('5'))) state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('5')))
} }
skipItems(4) skipItems(4)
awaitItem().also { state -> awaitItem().also { state ->
@ -77,10 +77,10 @@ class PinUnlockPresenterTest {
} }
val numberOfAttempts = initialState.remainingAttempts.dataOrNull() ?: 0 val numberOfAttempts = initialState.remainingAttempts.dataOrNull() ?: 0
repeat(numberOfAttempts) { repeat(numberOfAttempts) {
initialState.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('1'))) initialState.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('1')))
initialState.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('2'))) initialState.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('2')))
initialState.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('3'))) initialState.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('3')))
initialState.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('4'))) initialState.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('4')))
} }
skipItems(4 * numberOfAttempts + 2) skipItems(4 * numberOfAttempts + 2)
awaitItem().also { state -> awaitItem().also { state ->
@ -101,20 +101,20 @@ class PinUnlockPresenterTest {
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.pinEntry).isInstanceOf(AsyncData.Success::class.java) assertThat(state.pinEntry).isInstanceOf(AsyncData.Success::class.java)
assertThat(state.remainingAttempts).isInstanceOf(AsyncData.Success::class.java) assertThat(state.remainingAttempts).isInstanceOf(AsyncData.Success::class.java)
state.eventSink(PinUnlockEvents.OnForgetPin) state.eventSink(PinUnlockEvent.OnForgetPin)
} }
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.showSignOutPrompt).isTrue() assertThat(state.showSignOutPrompt).isTrue()
assertThat(state.isSignOutPromptCancellable).isTrue() assertThat(state.isSignOutPromptCancellable).isTrue()
state.eventSink(PinUnlockEvents.ClearSignOutPrompt) state.eventSink(PinUnlockEvent.ClearSignOutPrompt)
} }
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.showSignOutPrompt).isFalse() assertThat(state.showSignOutPrompt).isFalse()
state.eventSink(PinUnlockEvents.OnForgetPin) state.eventSink(PinUnlockEvent.OnForgetPin)
} }
awaitItem().also { state -> awaitItem().also { state ->
assertThat(state.showSignOutPrompt).isTrue() assertThat(state.showSignOutPrompt).isTrue()
state.eventSink(PinUnlockEvents.SignOut) state.eventSink(PinUnlockEvent.SignOut)
} }
skipItems(2) skipItems(2)
awaitItem().also { state -> awaitItem().also { state ->