Skip to content

Commit ba43dab

Browse files
committed
Simpler, better back button handling.
`OverlayDialogHolder` now can provide an `onBackPressed: () -> Unit` function to be called in place of `Dialog.onBackPressed`, and we provide a default implementation that hooks in `OnBackPressedDispatcher`. This works for all kinds of dialogs, not just modal ones, and replaces `ModalScreenOverlayBackButtonHelper`. The other method that was on `ModalScreenOverlayBackButtonHelper`, which put the no-op `backPressedHandler` on the content view to ensure those on layer layers would not fire, is now built into the default implementation of `ScreenOverlayDialogFactory.buildDialogWithContent`, which is designed to be customized anyway. Also locks down `AlertOverlayDialogFactory`, but moves all the interesting parts into a public function. Should be easier and safer for re-use. So how is this simpler? Or at least less complex? The old scheme had: - a strategy object in the `ViewEnvironment`, `ModalScreenOverlayBackButtonHelper`, that was providing a one-size-fits-all-dialogs scheme for back button handling - that scheme was only used by the `ScreenViewOverlay` subtype, not for `Overlay` in general - and it was applied only for modals, which was very wrong With this change: - The individual `OverlayDialogHolder` that wraps a `Dialog` is directly responsible for defining its back button behavior, and can be changed from the default behavior directly by the feature developer - The bizarre subtle `onBackPressed = {}` hack is moved to the factory class responsible for building the dialog, again in a spot that is naturally open for customization - and we eliminate the boolean return value, meaning one less decision point for developers
1 parent 2594db0 commit ba43dab

File tree

13 files changed

+187
-290
lines changed

13 files changed

+187
-290
lines changed

samples/tictactoe/app/src/androidTest/java/com/squareup/sample/TicTacToeEspressoTest.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,27 @@ class TicTacToeEspressoTest {
112112
.check(matches(isDisplayed()))
113113
}
114114

115+
@Test fun canGoBackFromAlert() {
116+
inAnyView(withId(R.id.login_email)).type("foo@bar")
117+
inAnyView(withId(R.id.login_password)).type("password")
118+
inAnyView(withId(R.id.login_button)).perform(click())
119+
120+
inAnyView(withId(R.id.player_X)).type("Mister X")
121+
inAnyView(withId(R.id.player_O)).type("Sister O")
122+
inAnyView(withId(R.id.start_game)).perform(click())
123+
124+
actuallyPressBack()
125+
inAnyView(withText("Do you really want to concede the game?"))
126+
.check(matches(isDisplayed()))
127+
inAnyView(withText("I QUIT")).perform(click())
128+
inAnyView(withText("Really?"))
129+
.check(matches(isDisplayed()))
130+
131+
actuallyPressBack()
132+
// Click a game cell to confirm the alert went away.
133+
clickCell(0)
134+
}
135+
115136
@Test fun canGoBackInModalViewAndSeeRestoredViewState() {
116137
// Log in and hit the 2fa screen.
117138
inAnyView(withId(R.id.login_email)).type("foo@2fa")

workflow-ui/core-android/api/core-android.api

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -429,16 +429,19 @@ public final class com/squareup/workflow1/ui/container/AlertDialogThemeResId : c
429429
public synthetic fun getDefault ()Ljava/lang/Object;
430430
}
431431

432-
public class com/squareup/workflow1/ui/container/AlertOverlayDialogFactory : com/squareup/workflow1/ui/container/OverlayDialogFactory {
432+
public final class com/squareup/workflow1/ui/container/AlertOverlayDialogFactory : com/squareup/workflow1/ui/container/OverlayDialogFactory {
433433
public fun <init> ()V
434434
public fun buildDialog (Lcom/squareup/workflow1/ui/container/AlertOverlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
435435
public synthetic fun buildDialog (Lcom/squareup/workflow1/ui/container/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
436436
public fun getType ()Lkotlin/reflect/KClass;
437437
}
438438

439+
public final class com/squareup/workflow1/ui/container/AlertOverlayDialogFactoryKt {
440+
public static final fun toDialogHolder (Landroid/app/AlertDialog;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
441+
}
442+
439443
public final class com/squareup/workflow1/ui/container/AndroidDialogBoundsKt {
440444
public static final fun maintainBounds (Landroid/app/Dialog;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function1;)V
441-
public static final fun maintainBounds (Landroid/app/Dialog;Lkotlinx/coroutines/flow/StateFlow;Lkotlin/jvm/functions/Function1;)V
442445
public static final fun setBounds (Landroid/app/Dialog;Landroid/graphics/Rect;)V
443446
}
444447

@@ -612,26 +615,6 @@ public final class com/squareup/workflow1/ui/container/LayeredDialogSessions$Sav
612615
public synthetic fun newArray (I)[Ljava/lang/Object;
613616
}
614617

615-
public abstract interface class com/squareup/workflow1/ui/container/ModalScreenOverlayBackButtonHelper {
616-
public static final field Companion Lcom/squareup/workflow1/ui/container/ModalScreenOverlayBackButtonHelper$Companion;
617-
public abstract fun onBackPressed (Landroid/view/View;)Z
618-
public abstract fun onContentViewUpdate (Landroid/view/View;)V
619-
}
620-
621-
public final class com/squareup/workflow1/ui/container/ModalScreenOverlayBackButtonHelper$Companion : com/squareup/workflow1/ui/ViewEnvironmentKey {
622-
public fun getDefault ()Lcom/squareup/workflow1/ui/container/ModalScreenOverlayBackButtonHelper;
623-
public synthetic fun getDefault ()Ljava/lang/Object;
624-
}
625-
626-
public final class com/squareup/workflow1/ui/container/ModalScreenOverlayBackButtonHelper$DefaultImpls {
627-
public static fun onBackPressed (Lcom/squareup/workflow1/ui/container/ModalScreenOverlayBackButtonHelper;Landroid/view/View;)Z
628-
public static fun onContentViewUpdate (Lcom/squareup/workflow1/ui/container/ModalScreenOverlayBackButtonHelper;Landroid/view/View;)V
629-
}
630-
631-
public final class com/squareup/workflow1/ui/container/ModalScreenOverlayBackButtonHelperKt {
632-
public static final fun plus (Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/container/ModalScreenOverlayBackButtonHelper;)Lcom/squareup/workflow1/ui/ViewEnvironment;
633-
}
634-
635618
public final class com/squareup/workflow1/ui/container/OverlayArea {
636619
public static final field Companion Lcom/squareup/workflow1/ui/container/OverlayArea$Companion;
637620
public fun <init> (Lkotlinx/coroutines/flow/StateFlow;)V
@@ -673,6 +656,7 @@ public abstract interface class com/squareup/workflow1/ui/container/OverlayDialo
673656
public static final field Companion Lcom/squareup/workflow1/ui/container/OverlayDialogHolder$Companion;
674657
public abstract fun getDialog ()Landroid/app/Dialog;
675658
public abstract fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment;
659+
public abstract fun getOnBackPressed ()Lkotlin/jvm/functions/Function0;
676660
public abstract fun getOnUpdateBounds ()Lkotlin/jvm/functions/Function1;
677661
public abstract fun getRunner ()Lkotlin/jvm/functions/Function2;
678662
}
@@ -691,24 +675,24 @@ public final class com/squareup/workflow1/ui/container/OverlayDialogHolder$Compa
691675
}
692676

693677
public final class com/squareup/workflow1/ui/container/OverlayDialogHolderKt {
694-
public static final fun OverlayDialogHolder (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
695-
public static synthetic fun OverlayDialogHolder$default (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
678+
public static final fun OverlayDialogHolder (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
679+
public static synthetic fun OverlayDialogHolder$default (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
696680
public static final fun canShow (Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;Lcom/squareup/workflow1/ui/container/Overlay;)Z
697681
public static final fun getShowing (Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;)Lcom/squareup/workflow1/ui/container/Overlay;
698682
public static final fun show (Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;Lcom/squareup/workflow1/ui/container/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;)V
699683
}
700684

701685
public final class com/squareup/workflow1/ui/container/RealOverlayDialogHolder : com/squareup/workflow1/ui/container/OverlayDialogHolder {
702-
public fun <init> (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V
686+
public fun <init> (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;)V
703687
public fun getDialog ()Landroid/app/Dialog;
704688
public fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment;
689+
public fun getOnBackPressed ()Lkotlin/jvm/functions/Function0;
705690
public fun getOnUpdateBounds ()Lkotlin/jvm/functions/Function1;
706691
public fun getRunner ()Lkotlin/jvm/functions/Function2;
707692
}
708693

709694
public class com/squareup/workflow1/ui/container/ScreenOverlayDialogFactory : com/squareup/workflow1/ui/container/OverlayDialogFactory {
710695
public fun <init> (Lkotlin/reflect/KClass;)V
711-
public fun buildContent (Lcom/squareup/workflow1/ui/ScreenViewFactory;Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/ScreenViewHolder;
712696
public synthetic fun buildDialog (Lcom/squareup/workflow1/ui/container/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
713697
public final fun buildDialog (Lcom/squareup/workflow1/ui/container/ScreenOverlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
714698
public fun buildDialogWithContent (Lcom/squareup/workflow1/ui/container/ScreenOverlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/ScreenViewHolder;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;

workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/DialogIntegrationTest.kt

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.squareup.workflow1.ui.container
22

33
import android.app.Dialog
4-
import android.content.Context
54
import android.view.View
65
import android.widget.EditText
76
import androidx.activity.ComponentActivity
@@ -55,24 +54,17 @@ internal class DialogIntegrationTest {
5554
object : ScreenOverlayDialogFactory<ContentRendering, DialogRendering>(
5655
type = DialogRendering::class
5756
) {
58-
override fun buildContent(
59-
viewFactory: ScreenViewFactory<ContentRendering>,
60-
initialContent: ContentRendering,
61-
initialEnvironment: ViewEnvironment,
62-
context: Context
63-
): ScreenViewHolder<ContentRendering> =
64-
super.buildContent(viewFactory, initialContent, initialEnvironment, context).also {
65-
latestContentView = it.view
66-
}
67-
6857
override fun buildDialogWithContent(
6958
initialRendering: DialogRendering,
7059
initialEnvironment: ViewEnvironment,
7160
content: ScreenViewHolder<ContentRendering>
72-
): OverlayDialogHolder<DialogRendering> =
73-
super.buildDialogWithContent(initialRendering, initialEnvironment, content).also {
61+
): OverlayDialogHolder<DialogRendering> {
62+
latestContentView = content.view
63+
64+
return super.buildDialogWithContent(initialRendering, initialEnvironment, content).also {
7465
latestDialog = it.dialog
7566
}
67+
}
7668
}
7769
}
7870

workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewHolder.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public interface ScreenViewHolder<in ScreenT : Screen> {
2828
/**
2929
* The function that is run by [show] to update [view] with a new [Screen] rendering and
3030
* [ViewEnvironment].
31+
*
32+
* Prefer calling [show] to using this directly, to ensure that [Showing] is
33+
* maintained correctly, and [showing] keeps working.
3134
*/
3235
public val runner: ScreenViewRunner<ScreenT>
3336

@@ -103,6 +106,10 @@ public fun <ScreenT : Screen> ScreenViewHolder<ScreenT>.show(
103106
/**
104107
* Returns the [Screen] most recently used to update the receiver's [view][ScreenViewHolder.view]
105108
* via a call to [show].
109+
*
110+
* Note that the exact type of the returned [Screen] is likely not to match that of
111+
* the receiver's `ScreenT` type parameter, e.g. if a
112+
* [wrapping view factory][ScreenViewFactory.forWrapper] is in use.
106113
*/
107114
@WorkflowUiExperimentalApi
108115
public val ScreenViewHolder<*>.showing: Screen

workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/AlertOverlayDialogFactory.kt

Lines changed: 74 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -16,84 +16,98 @@ import com.squareup.workflow1.ui.container.AlertOverlay.Event.Canceled
1616
import kotlin.reflect.KClass
1717

1818
/**
19-
* Default [OverlayDialogFactory] for [AlertOverlay].
19+
* Default [OverlayDialogFactory] for [AlertOverlay], uses [AlertDialog].
20+
* See [AlertDialog.toDialogHolder] to use [AlertDialog] for other purposes.
2021
*
21-
* This class is non-final for ease of customization of [AlertOverlay] handling,
22-
* see [OverlayDialogFactoryFinder] for details.
22+
* - To customize [AlertDialog] theming, see [AlertDialogThemeResId]
23+
* - To customize how [AlertOverlay] is handled more generally, set up a
24+
* custom [OverlayDialogFactoryFinder].
2325
*/
2426
@WorkflowUiExperimentalApi
25-
public open class AlertOverlayDialogFactory : OverlayDialogFactory<AlertOverlay> {
27+
internal class AlertOverlayDialogFactory : OverlayDialogFactory<AlertOverlay> {
2628
override val type: KClass<AlertOverlay> = AlertOverlay::class
2729

2830
override fun buildDialog(
2931
initialRendering: AlertOverlay,
3032
initialEnvironment: ViewEnvironment,
3133
context: Context
32-
): OverlayDialogHolder<AlertOverlay> {
33-
return AlertDialog.Builder(context, initialEnvironment[AlertDialogThemeResId])
34-
.create().let { alertDialog ->
35-
for (button in Button.values()) {
36-
// We want to be able to update the alert while it's showing, including to maybe
37-
// show more buttons than were there originally. The API for Android's `AlertDialog`
38-
// makes you think you can do that, but it actually doesn't work. So we force
39-
// `AlertDialog.Builder` to show every possible button; then we hide them all;
40-
// and then we manage their visibility ourselves at update time.
41-
//
42-
// We also don't want Android to tear down the dialog without our say so --
43-
// again, we might need to update the thing. But there is a dismiss call
44-
// built in to click handlers put in place by `AlertDialog`. So, when we're
45-
// preflighting every possible button, we put garbage click handlers in place.
46-
// Then we replace them with our own, again at update time, by setting each live
47-
// button's click handler directly, without letting `AlertDialog` interfere.
48-
//
49-
// https://github.com/square/workflow-kotlin/issues/138
50-
//
51-
// Why " "? An empty string means no button.
52-
alertDialog.setButton(button.toId(), " ") { _, _ -> }
53-
}
34+
): OverlayDialogHolder<AlertOverlay> =
35+
AlertDialog.Builder(context, initialEnvironment[AlertDialogThemeResId])
36+
.create()
37+
.toDialogHolder(initialEnvironment)
38+
}
5439

55-
OverlayDialogHolder(
56-
initialEnvironment = initialEnvironment,
57-
dialog = alertDialog,
58-
onUpdateBounds = null
59-
) { rendering, _ ->
60-
with(alertDialog) {
61-
if (rendering.cancelable) {
62-
setOnCancelListener { rendering.onEvent(Canceled) }
63-
setCancelable(true)
64-
} else {
65-
setCancelable(false)
66-
}
40+
/**
41+
* Wraps the receiver in in an [OverlayDialogHolder] that is able to update its
42+
* buttons as new [AlertOverlay] renderings are received.
43+
*/
44+
@WorkflowUiExperimentalApi
45+
public fun AlertDialog.toDialogHolder(
46+
initialEnvironment: ViewEnvironment
47+
): OverlayDialogHolder<AlertOverlay> {
48+
for (button in Button.values()) {
49+
// We want to be able to update the alert while it's showing, including to maybe
50+
// show more buttons than were there originally. The API for Android's `AlertDialog`
51+
// makes you think you can do that, but it actually doesn't work. So we force
52+
// `AlertDialog.Builder` to show every possible button; then we hide them all;
53+
// and then we manage their visibility ourselves at update time.
54+
//
55+
// We also don't want Android to tear down the dialog without our say so --
56+
// again, we might need to update the thing. But there is a dismiss call
57+
// built in to click handlers put in place by `AlertDialog`. So, when we're
58+
// preflighting every possible button, we put garbage click handlers in place.
59+
// Then we replace them with our own, again at update time, by setting each live
60+
// button's click handler directly, without letting `AlertDialog` interfere.
61+
//
62+
// https://github.com/square/workflow-kotlin/issues/138
63+
//
64+
// Why " "? An empty string means no button.
65+
setButton(button.toId(), " ") { _, _ -> }
66+
}
67+
68+
return OverlayDialogHolder(
69+
initialEnvironment = initialEnvironment,
70+
dialog = this,
71+
onUpdateBounds = null,
72+
onBackPressed = null
73+
) { rendering, _ ->
74+
with(this) {
75+
if (rendering.cancelable) {
76+
setOnCancelListener { rendering.onEvent(Canceled) }
77+
setCancelable(true)
78+
} else {
79+
setCancelable(false)
80+
}
6781

68-
setMessage(rendering.message)
69-
setTitle(rendering.title)
82+
setMessage(rendering.message)
83+
setTitle(rendering.title)
7084

71-
// The buttons won't actually exist until the dialog is showing.
72-
if (isShowing) updateButtonsOnShow(rendering) else setOnShowListener {
73-
updateButtonsOnShow(rendering)
74-
}
75-
}
76-
}
85+
// The buttons won't actually exist until the dialog is showing.
86+
if (isShowing) updateButtonsOnShow(rendering) else setOnShowListener {
87+
updateButtonsOnShow(rendering)
7788
}
89+
}
7890
}
91+
}
7992

80-
private fun Button.toId(): Int = when (this) {
81-
POSITIVE -> DialogInterface.BUTTON_POSITIVE
82-
NEGATIVE -> DialogInterface.BUTTON_NEGATIVE
83-
NEUTRAL -> DialogInterface.BUTTON_NEUTRAL
84-
}
93+
@WorkflowUiExperimentalApi
94+
private fun Button.toId(): Int = when (this) {
95+
POSITIVE -> DialogInterface.BUTTON_POSITIVE
96+
NEGATIVE -> DialogInterface.BUTTON_NEGATIVE
97+
NEUTRAL -> DialogInterface.BUTTON_NEUTRAL
98+
}
8599

86-
private fun AlertDialog.updateButtonsOnShow(rendering: AlertOverlay) {
87-
setOnShowListener(null)
100+
@WorkflowUiExperimentalApi
101+
private fun AlertDialog.updateButtonsOnShow(rendering: AlertOverlay) {
102+
setOnShowListener(null)
88103

89-
for (button in Button.values()) getButton(button.toId()).visibility = GONE
104+
for (button in Button.values()) getButton(button.toId()).visibility = GONE
90105

91-
for (entry in rendering.buttons.entries) {
92-
getButton(entry.key.toId())?.apply {
93-
setOnClickListener { rendering.onEvent(ButtonClicked(entry.key)) }
94-
text = entry.value
95-
visibility = VISIBLE
96-
}
106+
for (entry in rendering.buttons.entries) {
107+
getButton(entry.key.toId())?.apply {
108+
setOnClickListener { rendering.onEvent(ButtonClicked(entry.key)) }
109+
text = entry.value
110+
visibility = VISIBLE
97111
}
98112
}
99113
}

workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/AndroidDialogBounds.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ internal fun <D : Dialog> D.maintainBounds(
4343
}
4444

4545
@WorkflowUiExperimentalApi
46-
internal fun <D : Dialog> D.maintainBounds(
46+
private fun <D : Dialog> D.maintainBounds(
4747
bounds: StateFlow<Rect>,
4848
onBoundsChange: (Rect) -> Unit
4949
) {

workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogSession.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import android.os.Parcel
55
import android.os.Parcelable
66
import android.os.Parcelable.Creator
77
import android.view.KeyEvent
8+
import android.view.KeyEvent.ACTION_UP
9+
import android.view.KeyEvent.KEYCODE_BACK
10+
import android.view.KeyEvent.KEYCODE_ESCAPE
811
import android.view.MotionEvent
912
import android.view.Window
1013
import androidx.core.view.doOnAttach
@@ -46,14 +49,17 @@ internal class DialogSession(
4649
* Wrap the given dialog holder to maintain [allowEvents] on each update.
4750
*/
4851
val holder: OverlayDialogHolder<Overlay> = OverlayDialogHolder(
49-
holder.environment, holder.dialog
52+
holder.environment, holder.dialog, holder.onUpdateBounds, holder.onBackPressed
5053
) { overlay, environment ->
5154
allowEvents = !environment[CoveredByModal]
5255
holder.show(overlay, environment)
5356
}
5457

5558
val savedStateRegistryKey = Compatible.keyFor(holder.showing, index.toString())
5659

60+
private val KeyEvent.isBackPress: Boolean
61+
get() = (keyCode == KEYCODE_BACK || keyCode == KEYCODE_ESCAPE) && action == ACTION_UP
62+
5763
fun showDialog(
5864
parentLifecycleOwner: LifecycleOwner,
5965
stateRegistryAggregator: WorkflowSavedStateRegistryAggregator
@@ -68,7 +74,18 @@ internal class DialogSession(
6874
}
6975

7076
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
71-
return !allowEvents || realWindowCallback.dispatchKeyEvent(event)
77+
// Consume all events if we've been told to do so.
78+
if (!allowEvents) return true
79+
80+
// If there is an onBackPressed handler invoke it instead of allowing
81+
// the normal machinery to call Dialog.onBackPressed.
82+
if (event.isBackPress) holder.onBackPressed?.let { onBackPressed ->
83+
onBackPressed.invoke()
84+
return true
85+
}
86+
87+
// Allow the usual handling, including the usual call to Dialog.onBackPressed.
88+
return realWindowCallback.dispatchKeyEvent(event)
7289
}
7390
}
7491
}

0 commit comments

Comments
 (0)