LockScreen : fix one more navigation issue
This commit is contained in:
parent
67bdb78f98
commit
04afa8c200
9 changed files with 73 additions and 42 deletions
|
|
@ -20,7 +20,6 @@ import android.os.Parcelable
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.composable.Children
|
||||
import com.bumble.appyx.core.lifecycle.subscribe
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
|
|
@ -30,8 +29,6 @@ import dagger.assisted.Assisted
|
|||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
|
||||
import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback
|
||||
import io.element.android.features.lockscreen.impl.pin.PinCodeManager
|
||||
import io.element.android.features.lockscreen.impl.settings.LockScreenSettingsFlowNode
|
||||
import io.element.android.features.lockscreen.impl.setup.LockScreenSetupFlowNode
|
||||
import io.element.android.features.lockscreen.impl.unlock.PinUnlockNode
|
||||
|
|
@ -46,7 +43,6 @@ import kotlinx.parcelize.Parcelize
|
|||
class LockScreenFlowNode @AssistedInject constructor(
|
||||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val pinCodeManager: PinCodeManager,
|
||||
) : BackstackNode<LockScreenFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = plugins.filterIsInstance(Inputs::class.java).first().initialNavTarget,
|
||||
|
|
@ -71,26 +67,14 @@ class LockScreenFlowNode @AssistedInject constructor(
|
|||
data object Settings : NavTarget
|
||||
}
|
||||
|
||||
private val pinCodeManagerCallback = object : DefaultPinCodeManagerCallback() {
|
||||
override fun onPinCodeCreated() {
|
||||
plugins<LockScreenEntryPoint.Callback>().forEach {
|
||||
it.onSetupCompleted()
|
||||
private class OnSetupDoneCallback(private val plugins: List<LockScreenEntryPoint.Callback>) : LockScreenSetupFlowNode.Callback {
|
||||
override fun onSetupDone() {
|
||||
plugins.forEach {
|
||||
it.onSetupDone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBuilt() {
|
||||
super.onBuilt()
|
||||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
pinCodeManager.addCallback(pinCodeManagerCallback)
|
||||
},
|
||||
onDestroy = {
|
||||
pinCodeManager.removeCallback(pinCodeManagerCallback)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
return when (navTarget) {
|
||||
NavTarget.Unlock -> {
|
||||
|
|
@ -98,7 +82,8 @@ class LockScreenFlowNode @AssistedInject constructor(
|
|||
createNode<PinUnlockNode>(buildContext, plugins = listOf(inputs))
|
||||
}
|
||||
NavTarget.Setup -> {
|
||||
createNode<LockScreenSetupFlowNode>(buildContext)
|
||||
val callback = OnSetupDoneCallback(plugins())
|
||||
createNode<LockScreenSetupFlowNode>(buildContext, plugins = listOf(callback))
|
||||
}
|
||||
NavTarget.Settings -> {
|
||||
createNode<LockScreenSettingsFlowNode>(buildContext)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.fragment.app.FragmentActivity
|
||||
import io.element.android.libraries.cryptography.api.EncryptionDecryptionService
|
||||
import io.element.android.libraries.cryptography.api.SecretKeyRepository
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import timber.log.Timber
|
||||
import java.security.InvalidKeyException
|
||||
|
|
@ -86,7 +87,12 @@ class DefaultBiometricUnlock(
|
|||
val callback = AuthenticationCallback(callbacks, deferredAuthenticationResult)
|
||||
val prompt = BiometricPrompt(activity, executor, callback)
|
||||
prompt.authenticate(promptInfo, cryptoObject)
|
||||
return deferredAuthenticationResult.await()
|
||||
return try {
|
||||
deferredAuthenticationResult.await()
|
||||
} catch (cancellation: CancellationException) {
|
||||
prompt.cancelAuthentication()
|
||||
BiometricUnlock.AuthenticationResult.Failure(cancellation)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(KeyPermanentlyInvalidatedException::class)
|
||||
|
|
@ -110,7 +116,6 @@ private class AuthenticationCallback(
|
|||
override fun onAuthenticationFailed() {
|
||||
super.onAuthenticationFailed()
|
||||
callbacks.forEach { it.onBiometricUnlockFailed(null) }
|
||||
deferredAuthenticationResult.complete(BiometricUnlock.AuthenticationResult.Failure(null))
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ import dagger.assisted.Assisted
|
|||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockManager
|
||||
import io.element.android.features.lockscreen.impl.biometric.DefaultBiometricUnlockCallback
|
||||
import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback
|
||||
import io.element.android.features.lockscreen.impl.pin.PinCodeManager
|
||||
import io.element.android.features.lockscreen.impl.setup.pin.SetupPinNode
|
||||
|
|
@ -76,9 +75,6 @@ class LockScreenSettingsFlowNode @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private val pinCodeManagerCallback = object : DefaultPinCodeManagerCallback() {
|
||||
override fun onPinCodeVerified() {
|
||||
backstack.newRoot(NavTarget.Settings)
|
||||
}
|
||||
|
||||
override fun onPinCodeRemoved() {
|
||||
navigateUp()
|
||||
|
|
@ -89,12 +85,6 @@ class LockScreenSettingsFlowNode @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private val biometricUnlockCallback = object : DefaultBiometricUnlockCallback() {
|
||||
override fun onBiometricUnlockSuccess() {
|
||||
backstack.newRoot(NavTarget.Settings)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBuilt() {
|
||||
super.onBuilt()
|
||||
lifecycleScope.launch {
|
||||
|
|
@ -108,11 +98,9 @@ class LockScreenSettingsFlowNode @AssistedInject constructor(
|
|||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
pinCodeManager.addCallback(pinCodeManagerCallback)
|
||||
biometricUnlockManager.addCallback(biometricUnlockCallback)
|
||||
},
|
||||
onDestroy = {
|
||||
pinCodeManager.removeCallback(pinCodeManagerCallback)
|
||||
biometricUnlockManager.removeCallback(biometricUnlockCallback)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -121,7 +109,12 @@ class LockScreenSettingsFlowNode @AssistedInject constructor(
|
|||
return when (navTarget) {
|
||||
NavTarget.Unlock -> {
|
||||
val inputs = PinUnlockNode.Inputs(isInAppUnlock = true)
|
||||
createNode<PinUnlockNode>(buildContext, plugins = listOf(inputs))
|
||||
val callback = object : PinUnlockNode.Callback {
|
||||
override fun onUnlock() {
|
||||
backstack.newRoot(NavTarget.Settings)
|
||||
}
|
||||
}
|
||||
createNode<PinUnlockNode>(buildContext, plugins = listOf(inputs, callback))
|
||||
}
|
||||
NavTarget.SetupPin -> {
|
||||
createNode<SetupPinNode>(buildContext)
|
||||
|
|
|
|||
|
|
@ -17,10 +17,12 @@
|
|||
package io.element.android.features.lockscreen.impl.unlock
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import com.bumble.appyx.core.plugin.plugins
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.anvilannotations.ContributesNode
|
||||
|
|
@ -35,15 +37,30 @@ class PinUnlockNode @AssistedInject constructor(
|
|||
private val presenter: PinUnlockPresenter,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun onUnlock()
|
||||
}
|
||||
|
||||
data class Inputs(
|
||||
val isInAppUnlock: Boolean
|
||||
) : NodeInputs
|
||||
|
||||
private val inputs: Inputs = inputs()
|
||||
|
||||
private fun onUnlock() {
|
||||
plugins<Callback>().forEach {
|
||||
it.onUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
LaunchedEffect(state.isUnlocked) {
|
||||
if (state.isUnlocked) {
|
||||
onUnlock()
|
||||
}
|
||||
}
|
||||
PinUnlockView(
|
||||
state = state,
|
||||
isInAppUnlock = inputs.isInAppUnlock,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package io.element.android.features.lockscreen.impl.unlock
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -26,6 +27,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlock
|
||||
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockManager
|
||||
import io.element.android.features.lockscreen.impl.biometric.DefaultBiometricUnlockCallback
|
||||
import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback
|
||||
import io.element.android.features.lockscreen.impl.pin.PinCodeManager
|
||||
import io.element.android.features.lockscreen.impl.pin.model.PinEntry
|
||||
import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel
|
||||
|
|
@ -36,6 +39,7 @@ import io.element.android.libraries.core.bool.orFalse
|
|||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class PinUnlockPresenter @Inject constructor(
|
||||
|
|
@ -66,9 +70,10 @@ class PinUnlockPresenter @Inject constructor(
|
|||
var biometricUnlockResult by remember {
|
||||
mutableStateOf<BiometricUnlock.AuthenticationResult?>(null)
|
||||
}
|
||||
|
||||
val isUnlocked = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
val biometricUnlock = biometricUnlockManager.rememberBiometricUnlock()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
suspend {
|
||||
val pinCodeSize = pinCodeManager.getPinCodeSize()
|
||||
|
|
@ -94,7 +99,7 @@ class PinUnlockPresenter @Inject constructor(
|
|||
showSignOutPrompt = true
|
||||
}
|
||||
}
|
||||
|
||||
IsUnlockedEffect(isUnlocked)
|
||||
fun handleEvents(event: PinUnlockEvents) {
|
||||
when (event) {
|
||||
is PinUnlockEvents.OnPinKeypadPressed -> {
|
||||
|
|
@ -129,10 +134,33 @@ class PinUnlockPresenter @Inject constructor(
|
|||
signOutAction = signOutAction.value,
|
||||
showBiometricUnlock = biometricUnlock.isActive,
|
||||
biometricUnlockResult = biometricUnlockResult,
|
||||
isUnlocked = isUnlocked.value,
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IsUnlockedEffect(isUnlocked: MutableState<Boolean>) {
|
||||
DisposableEffect(Unit) {
|
||||
val biometricUnlockCallback = object : DefaultBiometricUnlockCallback() {
|
||||
override fun onBiometricUnlockSuccess() {
|
||||
isUnlocked.value = true
|
||||
}
|
||||
}
|
||||
val pinCodeVerifiedCallback = object : DefaultPinCodeManagerCallback() {
|
||||
override fun onPinCodeVerified() {
|
||||
isUnlocked.value = true
|
||||
}
|
||||
}
|
||||
biometricUnlockManager.addCallback(biometricUnlockCallback)
|
||||
pinCodeManager.addCallback(pinCodeVerifiedCallback)
|
||||
onDispose {
|
||||
biometricUnlockManager.removeCallback(biometricUnlockCallback)
|
||||
pinCodeManager.removeCallback(pinCodeVerifiedCallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Async<PinEntry>.isComplete(): Boolean {
|
||||
return dataOrNull()?.isComplete().orFalse()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ data class PinUnlockState(
|
|||
val showSignOutPrompt: Boolean,
|
||||
val signOutAction: Async<String?>,
|
||||
val showBiometricUnlock: Boolean,
|
||||
val isUnlocked: Boolean,
|
||||
val biometricUnlockResult: BiometricUnlock.AuthenticationResult?,
|
||||
val eventSink: (PinUnlockEvents) -> Unit
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ fun aPinUnlockState(
|
|||
showSignOutPrompt: Boolean = false,
|
||||
showBiometricUnlock: Boolean = true,
|
||||
biometricUnlockResult: BiometricUnlock.AuthenticationResult? = null,
|
||||
isUnlocked: Boolean = false,
|
||||
signOutAction: Async<String?> = Async.Uninitialized,
|
||||
) = PinUnlockState(
|
||||
pinEntry = Async.Success(pinEntry),
|
||||
|
|
@ -50,5 +51,6 @@ fun aPinUnlockState(
|
|||
showBiometricUnlock = showBiometricUnlock,
|
||||
signOutAction = signOutAction,
|
||||
biometricUnlockResult = biometricUnlockResult,
|
||||
isUnlocked = isUnlocked,
|
||||
eventSink = {}
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue