From 6c676a7d0595f128656b24661e30b8133eb88503 Mon Sep 17 00:00:00 2001 From: CherryPerry Date: Wed, 10 Aug 2022 16:53:34 +0100 Subject: [PATCH] Update BlockerExampleNode to use new back press API --- .../BlockerExampleBackPressInteractor.kt | 44 +++++++++++++ .../client/blocker/BlockerExampleNode.kt | 61 +++++++++++-------- 2 files changed, 80 insertions(+), 25 deletions(-) create mode 100644 sandbox/src/main/java/com/bumble/appyx/sandbox/client/blocker/BlockerExampleBackPressInteractor.kt diff --git a/sandbox/src/main/java/com/bumble/appyx/sandbox/client/blocker/BlockerExampleBackPressInteractor.kt b/sandbox/src/main/java/com/bumble/appyx/sandbox/client/blocker/BlockerExampleBackPressInteractor.kt new file mode 100644 index 0000000000..35114d8b03 --- /dev/null +++ b/sandbox/src/main/java/com/bumble/appyx/sandbox/client/blocker/BlockerExampleBackPressInteractor.kt @@ -0,0 +1,44 @@ +package com.bumble.appyx.sandbox.client.blocker + +import androidx.activity.OnBackPressedCallback +import androidx.compose.runtime.Stable +import com.bumble.appyx.core.plugin.BackPressHandler +import com.bumble.appyx.core.plugin.Destroyable +import com.bumble.appyx.core.plugin.UpNavigationHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch + +@Stable +class BlockerExampleBackPressInteractor : BackPressHandler, UpNavigationHandler, Destroyable { + private val scope = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob()) + + val interceptBackClicks: MutableStateFlow = MutableStateFlow(true) + val allowNavigateUp: MutableStateFlow = MutableStateFlow(true) + val errors: MutableSharedFlow = MutableSharedFlow(extraBufferCapacity = 1) + + override val onBackPressedCallback: OnBackPressedCallback = + object : OnBackPressedCallback(interceptBackClicks.value) { + override fun handleOnBackPressed() { + errors.tryEmit(System.currentTimeMillis()) + } + } + + init { + scope.launch { + interceptBackClicks.collect { onBackPressedCallback.isEnabled = it } + } + } + + override fun destroy() { + scope.cancel() + } + + override fun handleUpNavigation(): Boolean = + !allowNavigateUp.value + +} \ No newline at end of file diff --git a/sandbox/src/main/java/com/bumble/appyx/sandbox/client/blocker/BlockerExampleNode.kt b/sandbox/src/main/java/com/bumble/appyx/sandbox/client/blocker/BlockerExampleNode.kt index cd704e9075..1278d088bd 100644 --- a/sandbox/src/main/java/com/bumble/appyx/sandbox/client/blocker/BlockerExampleNode.kt +++ b/sandbox/src/main/java/com/bumble/appyx/sandbox/client/blocker/BlockerExampleNode.kt @@ -2,7 +2,6 @@ package com.bumble.appyx.sandbox.client.blocker import android.os.Parcelable import android.widget.Toast -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -11,6 +10,7 @@ import androidx.compose.material.Button import androidx.compose.material.Checkbox import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -27,7 +27,6 @@ import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.routingsource.backstack.BackStack import com.bumble.appyx.routingsource.backstack.operation.push import com.bumble.appyx.sandbox.client.child.ChildNode -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.parcelize.Parcelize import java.util.UUID @@ -36,17 +35,17 @@ class BlockerExampleNode( private val backStack: BackStack = BackStack( savedStateMap = buildContext.savedStateMap, initialElement = Child(UUID.randomUUID().toString()), - ) + ), + private val interactor: BlockerExampleBackPressInteractor = BlockerExampleBackPressInteractor(), ) : ParentNode( buildContext = buildContext, routingSource = backStack, + plugins = listOf(interactor), ) { @Parcelize data class Child(val id: String) : Parcelable - private val allowNavigateUpFromChildren = MutableStateFlow(true) - override fun resolve(routing: Child, buildContext: BuildContext): Node = ChildNode(routing.id, buildContext) @@ -54,19 +53,7 @@ class BlockerExampleNode( override fun View(modifier: Modifier) { Column(modifier = Modifier.fillMaxWidth()) { - val context = LocalContext.current - var lastToastClickTime by remember { mutableStateOf(0L) } - BackHandler { - val time = System.currentTimeMillis() - if (time - lastToastClickTime > 5000L) { - Toast.makeText( - context, - "It is a blocker, you can't go back by yourself, use 'go up'", - Toast.LENGTH_SHORT, - ).show() - lastToastClickTime = time - } - } + DisplayBackError() Button(onClick = { backStack.push(Child(UUID.randomUUID().toString())) }) { Text(text = "Push child") @@ -77,10 +64,22 @@ class BlockerExampleNode( modifier = Modifier.fillMaxWidth(), ) { Text(text = "Allow navigate up events from children") - val checked by allowNavigateUpFromChildren.collectAsState() + val checked by interactor.allowNavigateUp.collectAsState() + Checkbox( + checked = checked, + onCheckedChange = { interactor.allowNavigateUp.value = it }, + ) + } + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth(), + ) { + Text(text = "Block back presses") + val checked by interactor.interceptBackClicks.collectAsState() Checkbox( checked = checked, - onCheckedChange = { allowNavigateUpFromChildren.value = it }, + onCheckedChange = { interactor.interceptBackClicks.value = it }, ) } @@ -93,11 +92,23 @@ class BlockerExampleNode( } } - override fun performUpNavigation(): Boolean = - if (allowNavigateUpFromChildren.value) { - super.performUpNavigation() - } else { - true + @Composable + private fun DisplayBackError() { + val context = LocalContext.current + var lastToastClickTime by remember { mutableStateOf(0L) } + LaunchedEffect(context) { + interactor.errors.collect { + val time = System.currentTimeMillis() + if (time - lastToastClickTime > 3000L) { + Toast.makeText( + context, + "It is a blocker, you can't go back by yourself, use 'go up'", + Toast.LENGTH_SHORT, + ).show() + lastToastClickTime = time + } + } } + } }