From 34941f6163f67a1a02b3de81b437b9bfb69e0bc5 Mon Sep 17 00:00:00 2001 From: Veyndan Stuart Date: Thu, 9 Nov 2023 16:53:32 +0100 Subject: [PATCH] Propagate `BackHandler#enabled` changing in Treehouse --- .../redwood/treehouse/treehouseCompose.kt | 15 ++++++++++---- .../composeui/TreehouseContent.android.kt | 4 ++++ .../redwood/treehouse/TreehouseAppContent.kt | 20 ++++++++++++++----- .../treehouse/FakeZiplineTreehouseUi.kt | 3 ++- redwood-treehouse/api/zipline-api.toml | 4 ++-- .../treehouse/OnBackPressedCallbackService.kt | 3 ++- 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/redwood-treehouse-guest/src/commonMain/kotlin/app/cash/redwood/treehouse/treehouseCompose.kt b/redwood-treehouse-guest/src/commonMain/kotlin/app/cash/redwood/treehouse/treehouseCompose.kt index fd8c0f70e8..6362452ebc 100644 --- a/redwood-treehouse-guest/src/commonMain/kotlin/app/cash/redwood/treehouse/treehouseCompose.kt +++ b/redwood-treehouse-guest/src/commonMain/kotlin/app/cash/redwood/treehouse/treehouseCompose.kt @@ -27,6 +27,7 @@ import app.cash.redwood.ui.OnBackPressedDispatcher import app.cash.redwood.ui.UiConfiguration import app.cash.zipline.ZiplineScope import app.cash.zipline.ZiplineScoped +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.plus @@ -135,15 +136,21 @@ private fun ZiplineTreehouseUi.Host.asOnBackPressedDispatcher() = object : OnBac } private fun OnBackPressedCallback.asService() = object : OnBackPressedCallbackService { - override var isEnabled: Boolean - get() = this@asService.isEnabled - set(value) { - this@asService.isEnabled = value + override val isEnabled = MutableStateFlow(this@asService.isEnabled) + + init { + enabledChangedCallback = { + isEnabled.value = this@asService.isEnabled } + } override fun handleOnBackPressed() { this@asService.handleOnBackPressed() } + + override fun close() { + enabledChangedCallback = null + } } private object NullOnBackPressedDispatcherService : OnBackPressedDispatcherService { diff --git a/redwood-treehouse-host-composeui/src/androidMain/kotlin/app/cash/redwood/treehouse/composeui/TreehouseContent.android.kt b/redwood-treehouse-host-composeui/src/androidMain/kotlin/app/cash/redwood/treehouse/composeui/TreehouseContent.android.kt index f97857b813..0447918aca 100644 --- a/redwood-treehouse-host-composeui/src/androidMain/kotlin/app/cash/redwood/treehouse/composeui/TreehouseContent.android.kt +++ b/redwood-treehouse-host-composeui/src/androidMain/kotlin/app/cash/redwood/treehouse/composeui/TreehouseContent.android.kt @@ -30,9 +30,13 @@ internal actual fun platformOnBackPressedDispatcher(): RedwoodOnBackPressedDispa object : RedwoodOnBackPressedDispatcher { override fun addCallback(onBackPressedCallback: RedwoodOnBackPressedCallback): Cancellable { val androidOnBackPressedCallback = onBackPressedCallback.toAndroid() + onBackPressedCallback.enabledChangedCallback = { + androidOnBackPressedCallback.isEnabled = onBackPressedCallback.isEnabled + } delegate.addCallback(androidOnBackPressedCallback) return object : Cancellable { override fun cancel() { + onBackPressedCallback.enabledChangedCallback = null androidOnBackPressedCallback.remove() } } diff --git a/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/TreehouseAppContent.kt b/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/TreehouseAppContent.kt index 5b53a080c6..d3426461f7 100644 --- a/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/TreehouseAppContent.kt +++ b/redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/TreehouseAppContent.kt @@ -35,6 +35,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.job import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine private class State( val viewState: ViewState, @@ -457,20 +458,29 @@ private class ViewContentCodeBinding( onBackPressedCallbackService: OnBackPressedCallbackService, ): CancellableService { dispatchers.checkZipline() - val cancellable = onBackPressedDispatcher.addCallback( - object : OnBackPressedCallback(onBackPressedCallbackService.isEnabled) { + val cancellableJob = bindingScope.launch(dispatchers.zipline) { + val onBackPressedCallback = object : OnBackPressedCallback(onBackPressedCallbackService.isEnabled.value) { override fun handleOnBackPressed() { bindingScope.launch(dispatchers.zipline) { onBackPressedCallbackService.handleOnBackPressed() } } - }, - ) + } + val cancellable = onBackPressedDispatcher.addCallback(onBackPressedCallback) + launch { + onBackPressedCallbackService.isEnabled.collect { + onBackPressedCallback.isEnabled = it + } + } + suspendCancellableCoroutine { continuation -> + continuation.invokeOnCancellation { cancellable.cancel() } + } + } return object : CancellableService { override fun cancel() { dispatchers.checkZipline() - cancellable.cancel() + cancellableJob.cancel() } override fun close() { diff --git a/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/FakeZiplineTreehouseUi.kt b/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/FakeZiplineTreehouseUi.kt index 1374f87895..d30a1dd968 100644 --- a/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/FakeZiplineTreehouseUi.kt +++ b/redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/FakeZiplineTreehouseUi.kt @@ -24,6 +24,7 @@ import app.cash.redwood.protocol.PropertyChange import app.cash.redwood.protocol.PropertyTag import app.cash.redwood.protocol.WidgetTag import app.cash.redwood.ui.UiConfiguration +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.serialization.json.JsonPrimitive @@ -62,7 +63,7 @@ class FakeZiplineTreehouseUi( fun addBackHandler(isEnabled: Boolean): CancellableService { val result = host.addOnBackPressedCallback(object : OnBackPressedCallbackService { - override var isEnabled = isEnabled + override var isEnabled = MutableStateFlow(isEnabled) override fun handleOnBackPressed() { eventLog += "$name.onBackPressed()" diff --git a/redwood-treehouse/api/zipline-api.toml b/redwood-treehouse/api/zipline-api.toml index 8e26976876..f7a206b5aa 100644 --- a/redwood-treehouse/api/zipline-api.toml +++ b/redwood-treehouse/api/zipline-api.toml @@ -69,8 +69,8 @@ functions = [ # fun handleOnBackPressed(): kotlin.Unit "NjIN59uX", - # var isEnabled: kotlin.Boolean - "yKh+yo1c", + # val isEnabled: kotlinx.coroutines.flow.StateFlow + "TAJYS/cz", ] [app.cash.redwood.treehouse.OnBackPressedDispatcherService] diff --git a/redwood-treehouse/src/commonMain/kotlin/app/cash/redwood/treehouse/OnBackPressedCallbackService.kt b/redwood-treehouse/src/commonMain/kotlin/app/cash/redwood/treehouse/OnBackPressedCallbackService.kt index a29dd52b76..d802917de2 100644 --- a/redwood-treehouse/src/commonMain/kotlin/app/cash/redwood/treehouse/OnBackPressedCallbackService.kt +++ b/redwood-treehouse/src/commonMain/kotlin/app/cash/redwood/treehouse/OnBackPressedCallbackService.kt @@ -18,11 +18,12 @@ package app.cash.redwood.treehouse import app.cash.redwood.ui.OnBackPressedCallback import app.cash.zipline.ZiplineService import kotlin.native.ObjCName +import kotlinx.coroutines.flow.StateFlow /** Redwood's [OnBackPressedCallback] but implementing [ZiplineService]. */ @ObjCName("OnBackPressedCallbackService", exact = true) public interface OnBackPressedCallbackService : ZiplineService { - public var isEnabled: Boolean + public val isEnabled: StateFlow public fun handleOnBackPressed() }