From 54cd62ab76ad598f17dd90d127e49407b026bf59 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 25 Oct 2023 11:15:15 +0200 Subject: [PATCH] PIN: branch SignOut --- .../impl/DefaultLockScreenService.kt | 4 ++ .../lockscreen/impl/LockScreenFlowNode.kt | 4 +- .../settings/LockScreenSettingsFlowNode.kt | 4 +- .../impl/settings/LockScreenSettingsNode.kt | 4 +- .../lockscreen/impl/setup/SetupPinNode.kt | 4 +- .../lockscreen/impl/unlock/PinUnlockEvents.kt | 2 + .../lockscreen/impl/unlock/PinUnlockNode.kt | 4 +- .../impl/unlock/PinUnlockPresenter.kt | 25 +++++++ .../lockscreen/impl/unlock/PinUnlockState.kt | 1 + .../impl/unlock/PinUnlockStateProvider.kt | 3 + .../lockscreen/impl/unlock/PinUnlockView.kt | 67 +++++++++++++------ .../impl/src/main/res/values/localazy.xml | 6 ++ .../api/src/main/res/values/localazy.xml | 14 ++-- .../impl/src/main/res/values/localazy.xml | 3 +- .../src/main/res/values/localazy.xml | 8 ++- tools/localazy/config.json | 3 +- 16 files changed, 120 insertions(+), 36 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt index b949183892..afdc4e7899 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt @@ -56,6 +56,10 @@ class DefaultLockScreenService @Inject constructor( override fun onPinCodeVerified() { _lockScreenState.value = LockScreenLockState.Unlocked } + + override fun onPinCodeRemoved() { + _lockScreenState.value = LockScreenLockState.Unlocked + } }) coroutineScope.lockIfNeeded() observeAppForegroundState() diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt index a978b4d649..31a11d06d7 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/LockScreenFlowNode.kt @@ -34,10 +34,10 @@ import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode -import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize -@ContributesNode(AppScope::class) +@ContributesNode(SessionScope::class) class LockScreenFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt index 2d84db1b7e..63d1937317 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt @@ -38,11 +38,11 @@ import io.element.android.features.lockscreen.impl.unlock.PinUnlockNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode -import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SessionScope import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize -@ContributesNode(AppScope::class) +@ContributesNode(SessionScope::class) class LockScreenSettingsFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt index 3e19f24e68..96f5393483 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt @@ -25,9 +25,9 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SessionScope -@ContributesNode(AppScope::class) +@ContributesNode(SessionScope::class) class LockScreenSettingsNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinNode.kt index 762b9b4bc8..a1342c816b 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/SetupPinNode.kt @@ -24,9 +24,9 @@ import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SessionScope -@ContributesNode(AppScope::class) +@ContributesNode(SessionScope::class) class SetupPinNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt index 30ee16df02..18c45f5e4b 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt @@ -22,4 +22,6 @@ sealed interface PinUnlockEvents { data class OnPinKeypadPressed(val pinKeypadModel: PinKeypadModel) : PinUnlockEvents data object OnForgetPin : PinUnlockEvents data object ClearSignOutPrompt : PinUnlockEvents + data object SignOut : PinUnlockEvents + data object OnUseBiometric : PinUnlockEvents } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt index 0fba55c17b..88e076849e 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt @@ -24,9 +24,9 @@ import com.bumble.appyx.core.plugin.Plugin import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SessionScope -@ContributesNode(AppScope::class) +@ContributesNode(SessionScope::class) class PinUnlockNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt index c98ef75146..e73650314b 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt @@ -18,6 +18,7 @@ package io.element.android.features.lockscreen.impl.unlock import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -29,10 +30,16 @@ import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.runCatchingUpdatingState +import io.element.android.libraries.matrix.api.MatrixClient +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import javax.inject.Inject class PinUnlockPresenter @Inject constructor( private val pinCodeManager: PinCodeManager, + private val matrixClient: MatrixClient, + private val coroutineScope: CoroutineScope, ) : Presenter { @Composable @@ -51,6 +58,10 @@ class PinUnlockPresenter @Inject constructor( mutableStateOf(false) } + val signOutAction = remember { + mutableStateOf>(Async.Uninitialized) + } + LaunchedEffect(pinEntry) { if (pinEntry.isComplete()) { val isVerified = pinCodeManager.verifyPinCode(pinEntry.toText()) @@ -73,6 +84,13 @@ class PinUnlockPresenter @Inject constructor( } PinUnlockEvents.OnForgetPin -> showSignOutPrompt = true PinUnlockEvents.ClearSignOutPrompt -> showSignOutPrompt = false + PinUnlockEvents.SignOut -> { + showSignOutPrompt = false + coroutineScope.signOut(signOutAction) + } + PinUnlockEvents.OnUseBiometric -> { + //TODO + } } } return PinUnlockState( @@ -80,10 +98,17 @@ class PinUnlockPresenter @Inject constructor( showWrongPinTitle = showWrongPinTitle, remainingAttempts = remainingAttempts, showSignOutPrompt = showSignOutPrompt, + signOutAction = signOutAction.value, eventSink = ::handleEvents ) } + private fun CoroutineScope.signOut(signOutAction: MutableState>) = launch { + suspend { + matrixClient.logout() + }.runCatchingUpdatingState(signOutAction) + } + private fun PinEntry.process(pinKeypadModel: PinKeypadModel): PinEntry { return when (pinKeypadModel) { PinKeypadModel.Back -> deleteLast() diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt index c6f4dbb34d..fa80254cce 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt @@ -24,6 +24,7 @@ data class PinUnlockState( val showWrongPinTitle: Boolean, val remainingAttempts: Async, val showSignOutPrompt: Boolean, + val signOutAction: Async, val eventSink: (PinUnlockEvents) -> Unit ) { val isSignOutPromptCancellable = (remainingAttempts.dataOrNull() ?: 0) > 0 diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt index 3358c47fe4..cd2959dfe1 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt @@ -28,6 +28,7 @@ open class PinUnlockStateProvider : PreviewParameterProvider { aPinUnlockState(showWrongPinTitle = true), aPinUnlockState(showSignOutPrompt = true), aPinUnlockState(showSignOutPrompt = true, remainingAttempts = 0), + aPinUnlockState(signOutAction = Async.Loading()), ) } @@ -36,10 +37,12 @@ fun aPinUnlockState( remainingAttempts: Int = 3, showWrongPinTitle: Boolean = false, showSignOutPrompt: Boolean = false, + signOutAction: Async = Async.Uninitialized, ) = PinUnlockState( pinEntry = pinEntry, showWrongPinTitle = showWrongPinTitle, remainingAttempts = Async.Success(remainingAttempts), showSignOutPrompt = showSignOutPrompt, + signOutAction = signOutAction, eventSink = {} ) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt index c87bb00ec8..00a59e63cc 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt @@ -48,6 +48,8 @@ import io.element.android.features.lockscreen.impl.R import io.element.android.features.lockscreen.impl.pin.model.PinDigit import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypad +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview @@ -79,7 +81,13 @@ fun PinUnlockView( } val footer = @Composable { PinUnlockFooter( - modifier = Modifier.padding(top = 24.dp) + modifier = Modifier.padding(top = 24.dp), + onUseBiometric = { + state.eventSink(PinUnlockEvents.OnUseBiometric) + }, + onForgotPin = { + state.eventSink(PinUnlockEvents.OnForgetPin) + }, ) } val content = @Composable { constraints: BoxWithConstraintsScope -> @@ -107,23 +115,42 @@ fun PinUnlockView( modifier = commonModifier, ) } - if (state.showSignOutPrompt) { - if (state.isSignOutPromptCancellable) { - ConfirmationDialog( - title = stringResource(id = R.string.screen_app_lock_signout_alert_title), - content = stringResource(id = R.string.screen_app_lock_signout_alert_message), - onSubmitClicked = {}, - onDismiss = {}, - ) - } else { - ErrorDialog( - title = stringResource(id = R.string.screen_app_lock_signout_alert_title), - content = stringResource(id = R.string.screen_app_lock_signout_alert_message), - onDismiss = {}, - ) - } - } } + if (state.showSignOutPrompt) { + SignOutPrompt( + isCancellable = state.isSignOutPromptCancellable, + onSignOut = { state.eventSink(PinUnlockEvents.SignOut) }, + onDismiss = { state.eventSink(PinUnlockEvents.ClearSignOutPrompt) }, + ) + } + if (state.signOutAction is Async.Loading) { + ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) + } + } +} + +@Composable +private fun SignOutPrompt( + isCancellable: Boolean, + onSignOut: () -> Unit, + onDismiss: () -> Unit, + modifier: Modifier = Modifier +) { + if (isCancellable) { + ConfirmationDialog( + title = stringResource(id = R.string.screen_app_lock_signout_alert_title), + content = stringResource(id = R.string.screen_app_lock_signout_alert_message), + onSubmitClicked = onSignOut, + onDismiss = onDismiss, + modifier = modifier, + ) + } else { + ErrorDialog( + title = stringResource(id = R.string.screen_app_lock_signout_alert_title), + content = stringResource(id = R.string.screen_app_lock_signout_alert_message), + onDismiss = onSignOut, + modifier = modifier, + ) } } @@ -255,11 +282,13 @@ private fun PinUnlockHeader( @Composable private fun PinUnlockFooter( + onUseBiometric: ()->Unit, + onForgotPin: ()->Unit, modifier: Modifier = Modifier, ) { Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround) { - TextButton(text = "Use biometric", onClick = { }) - TextButton(text = stringResource(id = R.string.screen_app_lock_forgot_pin), onClick = { }) + TextButton(text = "Use biometric", onClick = onUseBiometric) + TextButton(text = stringResource(id = R.string.screen_app_lock_forgot_pin), onClick = onForgotPin) } } diff --git a/features/lockscreen/impl/src/main/res/values/localazy.xml b/features/lockscreen/impl/src/main/res/values/localazy.xml index 6b12eac427..529785b74d 100644 --- a/features/lockscreen/impl/src/main/res/values/localazy.xml +++ b/features/lockscreen/impl/src/main/res/values/localazy.xml @@ -4,12 +4,17 @@ "Wrong PIN. You have %1$d more chance" "Wrong PIN. You have %1$d more chances" + "biometric authentication" + "biometric unlock" "Forgot PIN?" "Change PIN code" "Allow biometric unlock" "Remove PIN" "Are you sure you want to remove PIN?" "Remove PIN?" + "Allow %1$s" + "I’d rather use PIN" + "Save yourself some time and use %1$s to unlock the app each time" "Choose PIN" "Confirm PIN" "You cannot choose this as your PIN code for security reasons" @@ -22,4 +27,5 @@ Choose something memorable. If you forget this PIN, you will be logged out of th "You’ll need to re-login and create a new PIN to proceed" "You are being signed out" "You have 3 attempts to unlock" + "Signing out…" diff --git a/features/logout/api/src/main/res/values/localazy.xml b/features/logout/api/src/main/res/values/localazy.xml index c695309194..9296381c87 100644 --- a/features/logout/api/src/main/res/values/localazy.xml +++ b/features/logout/api/src/main/res/values/localazy.xml @@ -1,12 +1,18 @@ - "Please wait for this to complete before signing out." - "Your keys are still being backed up" "Are you sure you want to sign out?" "Sign out" "Signing out…" - "You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages." - "Recovery not set up" + "You are about to sign out of your last session. If you sign out now, you will lose access to your encrypted messages." + "You have turned off backup" + "Your keys were still being backed up when you went offline. Reconnect so that your keys can be backed up before signing out." + "Your keys are still being backed up" + "Please wait for this to complete before signing out." + "Your keys are still being backed up" + "You are about to sign out of your last session. If you sign out now, you\'ll lose access to your encrypted messages." + "Recovery not set up" + "You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages." + "Have you saved your recovery key?" "Sign out" "Sign out" diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index bb285968c6..555ff574df 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -12,7 +12,8 @@ "Location" "Poll" "Text Formatting" - "Message history is currently unavailable in this room" + "Message history is currently unavailable." + "Message history is unavailable in this room. Verify this device to see your message history." "Could not retrieve user details" "Would you like to invite them back?" "You are alone in this chat" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index c70eceb21f..be27e21ff4 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -51,7 +51,7 @@ "No" "Not now" "OK" - "Open settings" + "Settings" "Open with" "Quick reply" "Quote" @@ -145,6 +145,7 @@ "Server URL" "Settings" "Shared location" + "Signing out" "Starting chat…" "Sticker" "Success" @@ -167,8 +168,11 @@ "Video" "Voice message" "Waiting…" + "Waiting for decryption key" "Are you sure you want to end this poll?" "Poll: %1$s" + "Your chat backup is currently out of sync. You need to confirm your recovery key to maintain access to your chat backup." + "Confirm your recovery key" "Confirmation" "Warning" "Activities" @@ -268,6 +272,8 @@ If you proceed, some of your settings may change." "Enter…" "Recovery key confirmed" "Confirm your recovery key" + "Copied recovery key" + "Generating…" "Save recovery key" "Write down your recovery key somewhere safe or save it in a password manager." "Tap to copy recovery key" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 3371b80814..5144dc191d 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -158,7 +158,8 @@ { "name": ":features:lockscreen:impl", "includeRegex": [ - "screen_app_lock_.*" + "screen_app_lock_.*", + "screen_signout_in_progress_dialog_content" ] } ]