Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3/4 fun View.setBackHandler() replaces val View.backPressedHandler #1028

Merged
merged 1 commit into from
Jun 26, 2023

Conversation

rjrjr
Copy link
Contributor

@rjrjr rjrjr commented Jun 14, 2023

val View.backPressedHandler is overly complicated and kind of naive. The replacement, fun View.setBackHandler(), carefully echoes the API and behavior of @Composable fun BackHandler(). We also provide a single argument overload that take a nullable handler function, and sets enabled to true for non-null handlers, false for null.

The biggest change in implementation is that we now use the preferred OnBackPressedDispatcher.addCallback(LifecycleOwner, OnBackPressedCallback) overload, which should allow AndroidX to do all the lifecycle bookkeeping for us -- taking advantage of all the hard work we've done to make WorkflowLifecycleOwner behave correctly.

This work revealed a problem where there was no WorkflowLifecycleOwner in place in time for the first update of the content view in ScreenOverlayDialogFactory, which prevented us from repeating that mistake in the new ComponentDialog.setContent() extension function introduced two PRs up. So that's nice.

@rjrjr rjrjr changed the title fun View.backHandler() replaces val View.backPressedHandler 3/3 fun View.backHandler() replaces val View.backPressedHandler Jun 14, 2023
// so becomes our parent.
WorkflowLifecycleOwner.installOn(
view,
initialEnvironment.onBackPressedDispatcherOwner(view)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will pull in the wrong OnBackPressedDispatcherOwner, but that was always the case. This problem is why we've deprecated ScreenOverlayDialogFactory and replaced it with fun ComponentDialog.setContent() -- it was fundamentally broken, requiring us to create the content view before the Dialog that would host it.

@rjrjr rjrjr changed the title 3/3 fun View.backHandler() replaces val View.backPressedHandler 3/3 fun View.backHandler() replaces val View.setBackPressedHandler Jun 14, 2023
@rjrjr rjrjr force-pushed the ray/3-new-back-handler branch from 4a789a8 to 053dccd Compare June 14, 2023 19:31
@rjrjr rjrjr changed the title 3/3 fun View.backHandler() replaces val View.setBackPressedHandler 3/3 fun View.setBackHandler() replaces val View.backPressedHandler Jun 14, 2023
@rjrjr rjrjr force-pushed the ray/2-install-on-back-dispatcher branch from d29aea2 to f62b52f Compare June 14, 2023 21:04
@rjrjr rjrjr force-pushed the ray/3-new-back-handler branch 4 times, most recently from c7194c9 to 6664cfe Compare June 14, 2023 21:34
@rjrjr rjrjr force-pushed the ray/2-install-on-back-dispatcher branch from f62b52f to 35ce30e Compare June 14, 2023 22:44
@rjrjr rjrjr force-pushed the ray/3-new-back-handler branch 3 times, most recently from f4b8dc7 to 8900743 Compare June 14, 2023 22:52
@rjrjr rjrjr force-pushed the ray/2-install-on-back-dispatcher branch from 35ce30e to dfe3296 Compare June 14, 2023 22:56
@rjrjr rjrjr force-pushed the ray/3-new-back-handler branch from 8900743 to df94fc9 Compare June 14, 2023 22:56
@rjrjr rjrjr force-pushed the ray/3-new-back-handler branch from df94fc9 to fcb88a7 Compare June 15, 2023 16:48
@rjrjr rjrjr force-pushed the ray/2-install-on-back-dispatcher branch from dfe3296 to faec646 Compare June 15, 2023 16:48
@rjrjr rjrjr marked this pull request as ready for review June 15, 2023 16:48
@rjrjr rjrjr requested review from a team and zach-klippenstein as code owners June 15, 2023 16:48
@rjrjr rjrjr force-pushed the ray/3-new-back-handler branch 2 times, most recently from bf2d754 to bb48afd Compare June 15, 2023 23:24
@rjrjr rjrjr force-pushed the ray/2-install-on-back-dispatcher branch from 78620bb to 08e8dd3 Compare June 15, 2023 23:24
@rjrjr rjrjr changed the title 3/3 fun View.setBackHandler() replaces val View.backPressedHandler 3/4 fun View.setBackHandler() replaces val View.backPressedHandler Jun 15, 2023
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should name this file SetBackHandler.kt

.around(scenarioRule)
.around(IdlingDispatcherRule)

private var viewHandlerCount = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
private var viewHandlerCount = 0
private var viewHandlerBackPressedCount = 0

@@ -13,6 +13,7 @@ import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.onBackPressedD
* A function passed to [View.backPressedHandler], to be called if the back
* button is pressed while that view is attached to a window.
*/
@Deprecated("Use View.backHandler()")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Deprecated("Use View.backHandler()")
@Deprecated("Use View.setBackHandler()")

@WorkflowUiExperimentalApi
@Deprecated("Use setOrClearBackHandler")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Deprecated("Use setOrClearBackHandler")
@Deprecated("Use setBackHandler")

I see you went through some naming iterations 😄

import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.onBackPressedDispatcherOwnerOrNull

/**
* A function to be called if the device back button is pressed while this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* A function to be called if the device back button is pressed while this
* Sets a function to be called if the device back button is pressed while this

onBack: () -> Unit
) {
val callback = onBackPressedCallbackOrNull ?: MutableOnBackPressedCallback().apply {
onBackPressedCallbackOrNull = this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is an interesting pattern. 🤔 .

Comment on lines +53 to +56
public fun View.setBackHandler(onBack: (() -> Unit)?) {
onBack?.let { setBackHandler(enabled = true, it) }
?: setBackHandler(enabled = false) {}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think setBackHandler(null) is the most understandable way of clearing/disabling this?

I personally like setBackHandler(enabled = false) and then making {} the default value of onBack in the version of the function above (not needing this overload then). I could see how you could end up with setBackHandler(false) but even that I don't find confusing.

Comment on lines +59 to +63
private var View.onBackPressedCallbackOrNull: MutableOnBackPressedCallback?
get() = getTag(R.id.view_back_handler) as MutableOnBackPressedCallback?
set(value) {
setTag(R.id.view_back_handler, value)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You truly are an Android view maestro how you nicely abstract this. 👨🏻‍🍳 💋

// decorView before updating, to ensure that the global androidx
// OnBackPressedDispatcher doesn't fire any set by lower layers. We put this
// in place before each call to show(), so the real content view will be able
// to clobber it.
if (modal) content.view.backPressedHandler = {}
if (modal) content.view.setBackHandler {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given all the no-op handlers we use, having a default of {} might be nice. Although maybe that's just in these internal use cases?

@rjrjr rjrjr force-pushed the ray/2-install-on-back-dispatcher branch from 08e8dd3 to d417e33 Compare June 21, 2023 20:30
@rjrjr rjrjr force-pushed the ray/3-new-back-handler branch from bb48afd to af3caaf Compare June 21, 2023 20:30
`val View.backPressedHandler` is overly complicated and kind of naive. The replacement, `fun View.setBackHandler()`, carefully echoes the API and behavior of [`@Composable fun BackHandler()`](https://developer.android.com/reference/kotlin/androidx/activity/compose/package-summary#BackHandler(kotlin.Boolean,kotlin.Function0)). We also provide a single argument overload that take a nullable handler function, and sets `enabled` to true for non-`null` handlers, false for `null`.

The biggest change in implementation is that we now use the preferred `OnBackPressedDispatcher.addCallback(LifecycleOwner, OnBackPressedCallback)` overload, which should allow AndroidX to do all the lifecycle bookkeeping for us -- taking advantage of all the hard work we've done to make `WorkflowLifecycleOwner` behave correctly.

This work revealed a problem where there was no `WorkflowLifecycleOwner` in place in time for the first update of the content view in `ScreenOverlayDialogFactory`, which prevented us from repeating that mistake in the new `ComponentDialog.setContent()` extension function introduced two PRs up. So that's nice.
@rjrjr rjrjr force-pushed the ray/3-new-back-handler branch from af3caaf to eb3de9c Compare June 24, 2023 00:51
@rjrjr rjrjr force-pushed the ray/2-install-on-back-dispatcher branch from d417e33 to 0335e65 Compare June 24, 2023 00:51
Base automatically changed from ray/2-install-on-back-dispatcher to ray/1-component-set-dialog June 26, 2023 21:40
@rjrjr rjrjr merged commit 2103b5f into ray/1-component-set-dialog Jun 26, 2023
@rjrjr rjrjr deleted the ray/3-new-back-handler branch June 26, 2023 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants