Skip to content

Commit fb67818

Browse files
committed
Adds a lot more kdoc about Overlay and Dialog.
1 parent cc9d4fd commit fb67818

File tree

10 files changed

+92
-23
lines changed

10 files changed

+92
-23
lines changed

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,6 @@ public class com/squareup/workflow1/ui/container/AlertOverlayDialogFactory : com
432432
public fun buildDialog (Lcom/squareup/workflow1/ui/container/AlertOverlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
433433
public synthetic fun buildDialog (Lcom/squareup/workflow1/ui/container/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;
434434
public fun getType ()Lkotlin/reflect/KClass;
435-
protected final fun toId (Lcom/squareup/workflow1/ui/container/AlertOverlay$Button;)I
436-
protected final fun updateButtonsOnShow (Landroid/app/AlertDialog;Lcom/squareup/workflow1/ui/container/AlertOverlay;)V
437435
}
438436

439437
public final class com/squareup/workflow1/ui/container/AndroidDialogBoundsKt {

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ import kotlin.reflect.KClass
2525
public open class AlertOverlayDialogFactory : OverlayDialogFactory<AlertOverlay> {
2626
override val type: KClass<AlertOverlay> = AlertOverlay::class
2727

28-
open override fun buildDialog(
28+
override fun buildDialog(
2929
initialRendering: AlertOverlay,
3030
initialEnvironment: ViewEnvironment,
3131
context: Context
3232
): OverlayDialogHolder<AlertOverlay> {
3333
return AlertDialog.Builder(context, initialEnvironment[AlertDialogThemeResId])
34-
.create().apply {
34+
.create().let { alertDialog ->
3535
for (button in Button.values()) {
3636
// We want to be able to update the alert while it's showing, including to maybe
3737
// show more buttons than were there originally. The API for Android's `AlertDialog`
@@ -41,18 +41,17 @@ public open class AlertOverlayDialogFactory : OverlayDialogFactory<AlertOverlay>
4141
//
4242
// We also don't want Android to tear down the dialog without our say so --
4343
// again, we might need to update the thing. But there is a dismiss call
44-
// built in to click handers put in place by `AlertDialog`. So, when we're
44+
// built in to click handlers put in place by `AlertDialog`. So, when we're
4545
// preflighting every possible button, we put garbage click handlers in place.
4646
// Then we replace them with our own, again at update time, by setting each live
4747
// button's click handler directly, without letting `AlertDialog` interfere.
4848
//
4949
// https://github.com/square/workflow-kotlin/issues/138
5050
//
5151
// Why " "? An empty string means no button.
52-
setButton(button.toId(), " ") { _, _ -> }
52+
alertDialog.setButton(button.toId(), " ") { _, _ -> }
5353
}
54-
}
55-
.let { alertDialog ->
54+
5655
OverlayDialogHolder(initialEnvironment, alertDialog) { rendering, _ ->
5756
with(alertDialog) {
5857
if (rendering.cancelable) {
@@ -74,13 +73,13 @@ public open class AlertOverlayDialogFactory : OverlayDialogFactory<AlertOverlay>
7473
}
7574
}
7675

77-
protected fun Button.toId(): Int = when (this) {
76+
private fun Button.toId(): Int = when (this) {
7877
POSITIVE -> DialogInterface.BUTTON_POSITIVE
7978
NEGATIVE -> DialogInterface.BUTTON_NEGATIVE
8079
NEUTRAL -> DialogInterface.BUTTON_NEUTRAL
8180
}
8281

83-
protected fun AlertDialog.updateButtonsOnShow(rendering: AlertOverlay) {
82+
private fun AlertDialog.updateButtonsOnShow(rendering: AlertOverlay) {
8483
setOnShowListener(null)
8584

8685
for (button in Button.values()) getButton(button.toId()).visibility = GONE

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
66
* Interface implemented by a rendering class to allow it to drive an Android UI
77
* via an appropriate [OverlayDialogFactory] implementation.
88
*
9+
* Note that you can mix this interface with others:
10+
*
11+
* - [ModalOverlay] to indicate that user input to covered views and dialogs
12+
* should be blocked while this one is visible.
13+
*
14+
* - [ScreenOverlay] for dialogs whose content is defined by a wrapped
15+
* [Screen][com.squareup.workflow1.ui.Screen] instance. And in this case,
16+
* also note [ScreenOverlayDialogFactory].
17+
*
918
* This is the simplest way to introduce a [Dialog][android.app.Dialog] workflow driven UI,
1019
* but using it requires your workflows code to reside in Android modules, instead
1120
* of pure Kotlin. If this is a problem, or you need more flexibility for any other

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ import com.squareup.workflow1.ui.ViewEnvironment
1919
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
2020
import com.squareup.workflow1.ui.WorkflowViewStub
2121

22+
/**
23+
* Default container for [Overlay] renderings, providing support for
24+
* orthogonal subtypes [ModalOverlay] and [ScreenOverlay]. As much
25+
* work as possible is delegated to the public [LayeredDialogSessions]
26+
* support class, to make it practical to write custom forks should
27+
* the need arise.
28+
*
29+
* See [ScreenOverlayDialogFactory] for a general overview of how
30+
* Workflow's [android.app.Dialog] support actually works.
31+
*/
2232
@WorkflowUiExperimentalApi
2333
internal class BodyAndOverlaysContainer @JvmOverloads constructor(
2434
context: Context,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import com.squareup.workflow1.ui.androidx.WorkflowSavedStateRegistryAggregator
1818

1919
/**
2020
* Used by [LayeredDialogSessions] to manage lifecycle and view persistence concerns for an
21-
* [OverlayDialogHolder], as well as enforcing modal behavior.
21+
* [OverlayDialogHolder], as well as enforcing modal behavior. See [LayeredDialogSessions]
22+
* for a general overview of the lifecycle of a managed [Dialog][android.app.Dialog].
2223
*/
2324
@WorkflowUiExperimentalApi
2425
internal class DialogSession(

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,50 @@ import kotlinx.coroutines.flow.StateFlow
2424
/**
2525
* Does the bulk of the work of maintaining a set of [Dialog][android.app.Dialog]s
2626
* to reflect lists of [Overlay]. Can be used to create custom [Overlay]-based
27-
* layouts if [BodyAndOverlaysScreen] or the default [View] bound to it are too restrictive.
28-
* Provides a [LifecycleOwner] per managed dialog, and view persistence support.
27+
* layouts if [BodyAndOverlaysScreen] or the default [BodyAndOverlaysContainer] bound
28+
* to it are too restrictive.
29+
*
30+
* Provides an [allowEvents] field that reflects the presence or absence of Dialogs driven
31+
* by [ModalOverlay], and makes [OverlayArea] available in the [ViewEnvironment],
32+
* which in turn drives calls to [ScreenOverlayDialogFactory.updateBounds].
33+
*
34+
* Provides a [ViewTreeLifecycleOwner] per managed Dialog, and view persistence support,
35+
* both for classic [View.onSaveInstanceState] and
36+
* Jetpack [SavedStateRegistry][androidx.savedstate.SavedStateRegistry].
37+
*
38+
* ## Lifecycle of a managed [Dialog][android.app.Dialog]
39+
*
40+
* When [update] is called with an [Overlay] that is not [Compatible] with an
41+
* existing Dialog at the same index, the appropriate [OverlayDialogFactory] instance
42+
* is fetched from the [ViewEnvironment]. That factory builds (but does not
43+
* [show][android.app.Dialog.show]) a new Dialog, wrapped in an [OverlayDialogHolder].
44+
* The holder in turn is held by a [DialogSession] instance. There is a 1:1:1 relationship
45+
* between the Dialog, the [OverlayDialogHolder] which can [update it][OverlayDialogHolder.show],
46+
* and the [DialogSession] that manages its [LifecycleOwner] and persistence.
47+
*
48+
* When a new [DialogSession] begins:
49+
*
50+
* - a [ViewTreeLifecycleOwner] is created as a child to the one provided
51+
* by [getParentLifecycleOwner]
52+
*
53+
* - An appropriately scoped [SavedStateRegistry][androidx.savedstate.SavedStateRegistry]
54+
* is put in place, provided that [onAttachedToWindow] and [onDetachedFromWindow] are
55+
* called from the like named methods of the nearest container View
56+
*
57+
* - Any available classic View state is restored to the new Dialog's content View tree,
58+
* provided that [onSaveInstanceState] and [onRestoreInstanceState] are called from the
59+
* like named methods of that container View
60+
*
61+
* - And the Dialog is [shown][android.app.Dialog.show]
62+
*
63+
* The [DialogSession] is maintained (and its two flavors of View state are recorded
64+
* as prompted by the Android runtime) so long as [update] is called with a
65+
* [Compatible] [Overlay] rendering in the same position.
66+
*
67+
* When [update] is called without a matching [Overlay], or the
68+
* [parent Lifecycle][getParentLifecycleOwner] ends, the [DialogSession] ends,
69+
* its [ViewTreeLifecycleOwner] is destroyed, and the Dialog is
70+
* [dismissed][android.app.Dialog.dismiss].
2971
*
3072
* @param bounds made available to managed dialogs via the [OverlayArea]
3173
* [ViewEnvironmentKey][com.squareup.workflow1.ui.ViewEnvironmentKey],
@@ -37,7 +79,6 @@ import kotlinx.coroutines.flow.StateFlow
3779
*
3880
* @param getParentLifecycleOwner provides the [LifecycleOwner] to serve as
3981
* an ancestor to those created for managed [Dialog][android.app.Dialog]s.
40-
*
4182
*/
4283
@WorkflowUiExperimentalApi
4384
public class LayeredDialogSessions private constructor(

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,17 @@ import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
99
/**
1010
* Factory for [Dialog] instances that can show renderings of type [OverlayT] : [Overlay].
1111
*
12-
* It's simplest to have your rendering classes implement [AndroidOverlay] to associate
12+
* Implement this interface directly for rendering types that are completely self-descriptive,
13+
* like [AlertOverlay]. For dialogs that wrap a [Screen][com.squareup.workflow1.ui.Screen]
14+
* for their content, implement [ScreenOverlayDialogFactory] interface and use the
15+
* [ScreenOverlay] rendering type.
16+
*
17+
* To minimize boilerplate, have your rendering classes implement [AndroidOverlay] to associate
1318
* them with appropriate an appropriate [OverlayDialogFactory]. For more flexibility, and to
1419
* avoid coupling your workflow directly to the Android runtime, see [ViewRegistry].
20+
*
21+
* See the kdoc on [ScreenOverlayDialogFactory] for a fuller description of how
22+
* [Dialog]s are placed on the screen, and their impact on UI events.
1523
*/
1624
@WorkflowUiExperimentalApi
1725
public interface OverlayDialogFactory<OverlayT : Overlay> : ViewRegistry.Entry<OverlayT> {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
88
internal class RealOverlayDialogHolder<OverlayT : Overlay>(
99
initialEnvironment: ViewEnvironment,
1010
override val dialog: Dialog,
11-
runner: (rendering: OverlayT, environment: ViewEnvironment) -> Unit
11+
runnerFunction: (rendering: OverlayT, environment: ViewEnvironment) -> Unit
1212
) : OverlayDialogHolder<OverlayT> {
1313

1414
private var _environment: ViewEnvironment = initialEnvironment
1515
override val environment: ViewEnvironment get() = _environment
1616

1717
override val runner = { newScreen: OverlayT, newEnvironment: ViewEnvironment ->
1818
_environment = newEnvironment
19-
runner(newScreen, newEnvironment)
19+
runnerFunction(newScreen, newEnvironment)
2020
}
2121
}

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,11 @@ import kotlin.reflect.KClass
3333
* and honor the [OverlayArea] and [CoveredByModal] values placed in
3434
* the [ViewEnvironment] by the standard [BodyAndOverlaysScreen] container.
3535
*
36-
* ## Implementation notes
36+
* ## Details of [Dialog] management
3737
*
38-
* Our Android dialog management story is complex. There is a lot of machinery in play to
39-
* to provide control over the placement of dialog windows, and to ensure that modal
40-
* dialogs behave as expected -- e.g., that events in the Activity window are blocked
41-
* the instant a modal dialog is shown.
38+
* There is a lot of machinery in play to to provide control over the placement of
39+
* [Dialog] windows, and to ensure that [ModalOverlay] dialogs behave as expected (i.e.,
40+
* that events in the Activity window are blocked the instant a modal [Dialog] is shown).
4241
*
4342
* For placement, consider a layout where we want the option to show a tutorial bar below
4443
* the main UI.

workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/container/Overlay.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
99
* An Overlay can be any part of the UI that visually floats in a layer above the main UI,
1010
* or above other Overlays. Possible examples include alerts, drawers, and tooltips.
1111
*
12-
* Note in particular that an Overlay is not necessarily a modal window.
12+
* Note in particular that an Overlay is not necessarily a modal window -- that is,
13+
* one that prevents covered views and windows from processing UI events.
1314
* Rendering types can opt into modality by extending [ModalOverlay].
15+
*
16+
* See [ScreenOverlay] to define an [Overlay] whose content is provided by a wrapped
17+
* [Screen][com.squareup.workflow1.ui.Screen].
1418
*/
1519
@WorkflowUiExperimentalApi
1620
public interface Overlay

0 commit comments

Comments
 (0)