diff --git a/samples/tictactoe/app/src/androidTest/java/com/squareup/sample/TicTacToeEspressoTest.kt b/samples/tictactoe/app/src/androidTest/java/com/squareup/sample/TicTacToeEspressoTest.kt index de0128b725..089548de71 100644 --- a/samples/tictactoe/app/src/androidTest/java/com/squareup/sample/TicTacToeEspressoTest.kt +++ b/samples/tictactoe/app/src/androidTest/java/com/squareup/sample/TicTacToeEspressoTest.kt @@ -42,7 +42,7 @@ import org.junit.runner.RunWith class TicTacToeEspressoTest { private val scenarioRule = ActivityScenarioRule(TicTacToeActivity::class.java) - @get:Rule val rules = RuleChain.outerRule(DetectLeaksAfterTestSuccess()) + @get:Rule val rules: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess()) .around(scenarioRule) .around(IdlingDispatcherRule) private val scenario get() = scenarioRule.scenario diff --git a/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/backstack/BackStackScreen.kt b/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/backstack/BackStackScreen.kt index 17377bfab4..eff095ef61 100644 --- a/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/backstack/BackStackScreen.kt +++ b/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/backstack/BackStackScreen.kt @@ -8,9 +8,6 @@ import com.squareup.workflow1.ui.asScreen import com.squareup.workflow1.ui.container.BackStackScreen as NewBackStackScreen /** - * **This will be deprecated in favor of - * [com.squareup.workflow1.ui.container.BackStackScreen] very soon.** - * * Represents an active screen ([top]), and a set of previously visited screens to which we may * return ([backStack]). By rendering the entire history we allow the UI to do things like maintain * cached view state, implement drag-back gestures without waiting for the workflow, etc. diff --git a/workflow-ui/core-android/api/core-android.api b/workflow-ui/core-android/api/core-android.api index 774b116628..976a9b9c73 100644 --- a/workflow-ui/core-android/api/core-android.api +++ b/workflow-ui/core-android/api/core-android.api @@ -191,25 +191,11 @@ public final class com/squareup/workflow1/ui/ScreenViewFactoryKt { } public abstract interface class com/squareup/workflow1/ui/ScreenViewHolder { - public static final field Companion Lcom/squareup/workflow1/ui/ScreenViewHolder$Companion; public abstract fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment; public abstract fun getRunner ()Lcom/squareup/workflow1/ui/ScreenViewRunner; public abstract fun getView ()Landroid/view/View; } -public final class com/squareup/workflow1/ui/ScreenViewHolder$Companion { -} - -public final class com/squareup/workflow1/ui/ScreenViewHolder$Companion$Showing : com/squareup/workflow1/ui/ViewEnvironmentKey { - public static final field INSTANCE Lcom/squareup/workflow1/ui/ScreenViewHolder$Companion$Showing; - public fun getDefault ()Lcom/squareup/workflow1/ui/Screen; - public synthetic fun getDefault ()Ljava/lang/Object; -} - -public final class com/squareup/workflow1/ui/ScreenViewHolder$Companion$ShowingNothing : com/squareup/workflow1/ui/Screen { - public static final field INSTANCE Lcom/squareup/workflow1/ui/ScreenViewHolder$Companion$ShowingNothing; -} - public final class com/squareup/workflow1/ui/ScreenViewHolderKt { public static final fun ScreenViewHolder (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/view/View;Lcom/squareup/workflow1/ui/ScreenViewRunner;)Lcom/squareup/workflow1/ui/ScreenViewHolder; public static final fun canShow (Lcom/squareup/workflow1/ui/ScreenViewHolder;Lcom/squareup/workflow1/ui/Screen;)Z @@ -277,10 +263,12 @@ public final class com/squareup/workflow1/ui/ViewShowRenderingKt { public static final fun getEnvironment (Landroid/view/View;)Lcom/squareup/workflow1/ui/ViewEnvironment; public static final fun getEnvironmentOrNull (Landroid/view/View;)Lcom/squareup/workflow1/ui/ViewEnvironment; public static final synthetic fun getRendering (Landroid/view/View;)Ljava/lang/Object; + public static final fun getScreen (Landroid/view/View;)Lcom/squareup/workflow1/ui/Screen; public static final fun getScreenOrNull (Landroid/view/View;)Lcom/squareup/workflow1/ui/Screen; public static final fun getShowRendering (Landroid/view/View;)Lkotlin/jvm/functions/Function2; public static final fun getStarter (Landroid/view/View;)Lkotlin/jvm/functions/Function1; public static final fun getStarterOrNull (Landroid/view/View;)Lkotlin/jvm/functions/Function1; + public static final fun setScreen (Landroid/view/View;Lcom/squareup/workflow1/ui/Screen;)V public static final fun setStarter (Landroid/view/View;Lkotlin/jvm/functions/Function1;)V public static final fun showRendering (Landroid/view/View;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;)V public static final fun start (Landroid/view/View;)V @@ -536,8 +524,16 @@ public final class com/squareup/workflow1/ui/container/CoveredByModal : com/squa public synthetic fun getDefault ()Ljava/lang/Object; } +public final class com/squareup/workflow1/ui/container/DialogOverlayKt { + public static final fun getDecorView (Landroid/app/Dialog;)Landroid/view/View; + public static final fun getDecorViewOrNull (Landroid/app/Dialog;)Landroid/view/View; + public static final fun getOverlay (Landroid/app/Dialog;)Lcom/squareup/workflow1/ui/container/Overlay; + public static final fun getOverlayOrNull (Landroid/app/Dialog;)Lcom/squareup/workflow1/ui/container/Overlay; + public static final fun setOverlay (Landroid/app/Dialog;Lcom/squareup/workflow1/ui/container/Overlay;)V +} + public final class com/squareup/workflow1/ui/container/DialogSession { - public fun (ILcom/squareup/workflow1/ui/container/OverlayDialogHolder;)V + public fun (ILcom/squareup/workflow1/ui/container/Overlay;Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;)V public final fun dismiss ()V public final fun getHolder ()Lcom/squareup/workflow1/ui/container/OverlayDialogHolder; public final fun getSavedStateRegistryKey ()Ljava/lang/String; @@ -653,7 +649,6 @@ public final class com/squareup/workflow1/ui/container/OverlayDialogFactoryKt { } public abstract interface class com/squareup/workflow1/ui/container/OverlayDialogHolder { - public static final field Companion Lcom/squareup/workflow1/ui/container/OverlayDialogHolder$Companion; public abstract fun getDialog ()Landroid/app/Dialog; public abstract fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment; public abstract fun getOnBackPressed ()Lkotlin/jvm/functions/Function0; @@ -661,19 +656,6 @@ public abstract interface class com/squareup/workflow1/ui/container/OverlayDialo public abstract fun getRunner ()Lkotlin/jvm/functions/Function2; } -public final class com/squareup/workflow1/ui/container/OverlayDialogHolder$Companion { -} - -public final class com/squareup/workflow1/ui/container/OverlayDialogHolder$Companion$InOverlay : com/squareup/workflow1/ui/ViewEnvironmentKey { - public static final field INSTANCE Lcom/squareup/workflow1/ui/container/OverlayDialogHolder$Companion$InOverlay; - public fun getDefault ()Lcom/squareup/workflow1/ui/container/Overlay; - public synthetic fun getDefault ()Ljava/lang/Object; -} - -public final class com/squareup/workflow1/ui/container/OverlayDialogHolder$Companion$NoOverlay : com/squareup/workflow1/ui/container/Overlay { - public static final field INSTANCE Lcom/squareup/workflow1/ui/container/OverlayDialogHolder$Companion$NoOverlay; -} - public final class com/squareup/workflow1/ui/container/OverlayDialogHolderKt { 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; 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; diff --git a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/DialogIntegrationTest.kt b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/DialogIntegrationTest.kt index f88280f01b..37ac732200 100644 --- a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/DialogIntegrationTest.kt +++ b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/DialogIntegrationTest.kt @@ -14,8 +14,6 @@ import com.squareup.workflow1.ui.ScreenViewHolder import com.squareup.workflow1.ui.ViewEnvironment import com.squareup.workflow1.ui.WorkflowLayout import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.container.OverlayDialogHolder.Companion.InOverlay -import com.squareup.workflow1.ui.environmentOrNull import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -104,7 +102,7 @@ internal class DialogIntegrationTest { scenario.onActivity { root.show(twoDialogs) - val lastOverlay = latestContentView?.environmentOrNull?.get(InOverlay)!! + val lastOverlay = latestDialog?.overlay assertThat(lastOverlay).isEqualTo(dialog2) } } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRegistry.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRegistry.kt index 49a582fbb8..b5b02e3d8c 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRegistry.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRegistry.kt @@ -10,10 +10,6 @@ import com.squareup.workflow1.ui.container.EnvironmentScreenLegacyViewFactory import kotlin.reflect.KClass /** - * **This will be deprecated in favor of - * [ScreenViewFactoryFinder.getViewFactoryForRendering] - * very soon.** - * * It is usually more convenient to use [WorkflowViewStub] or [DecorativeViewFactory] * than to call this method directly. * @@ -49,8 +45,6 @@ public fun ViewRegistry.getFactoryForRendering( } /** - * **This will be deprecated in favor of [ViewRegistry.getEntryFor] very soon.** - * * This method is not for general use, use [WorkflowViewStub] instead. * * Returns the [ViewFactory] that was registered for the given [renderingType], or null @@ -68,8 +62,6 @@ public fun ViewRegistry.getFactoryFor( } /** - * **This will be deprecated in favor of [ScreenViewFactory.startShowing] very soon.** - * * It is usually more convenient to use [WorkflowViewStub] or [DecorativeViewFactory] * than to call this method directly. * diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRendering.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRendering.kt index 0970156490..fccd64cb00 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRendering.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRendering.kt @@ -1,8 +1,6 @@ package com.squareup.workflow1.ui /** - * **This will be deprecated in favor of [AndroidScreen] very soon.** - * * Interface implemented by a rendering class to allow it to drive an Android UI * via an appropriate [ViewFactory] implementation. * diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BackButtonScreen.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BackButtonScreen.kt index b218f7b304..22b240589e 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BackButtonScreen.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BackButtonScreen.kt @@ -1,9 +1,6 @@ package com.squareup.workflow1.ui /** - * **This will be deprecated in favor of - * [com.squareup.workflow1.ui.container.BackButtonScreen] very soon.** - * * Adds optional back button handling to a [wrapped] rendering, possibly overriding that * the wrapped rendering's own back button handler. * diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BuilderViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BuilderViewFactory.kt index 830f9daf77..c38f72b3fe 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BuilderViewFactory.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BuilderViewFactory.kt @@ -6,8 +6,6 @@ import android.view.ViewGroup import kotlin.reflect.KClass /** - * **This will be deprecated in favor of [ScreenViewFactory.fromCode] very soon.** - * * A [ViewFactory] that creates [View]s that need to be generated from code. * (Use [LayoutRunner] to work with XML layout resources.) * diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/DecorativeViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/DecorativeViewFactory.kt index ae078116c7..03d53cbbcc 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/DecorativeViewFactory.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/DecorativeViewFactory.kt @@ -6,8 +6,6 @@ import android.view.ViewGroup import kotlin.reflect.KClass /** - * **This will be deprecated in favor of [ScreenViewFactory.forWrapper] very soon.** - * * A [ViewFactory] for [OuterT] that delegates view construction responsibilities * to the factory registered for [InnerT]. Makes it convenient for [OuterT] to wrap * instances of [InnerT] to add information or behavior, without requiring wasteful wrapping diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LayoutRunner.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LayoutRunner.kt index f4af83b77d..c3a36f20a5 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LayoutRunner.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LayoutRunner.kt @@ -6,8 +6,6 @@ import androidx.annotation.LayoutRes import androidx.viewbinding.ViewBinding /** - * **This will be deprecated in favor of [ScreenViewRunner] very soon.** - * * A delegate that implements a [showRendering] method to be called when a workflow rendering * of type [RenderingT] is ready to be displayed in a view inflated from a layout resource * by a [ViewRegistry]. (Use [BuilderViewFactory] if you want to build views from code rather diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewHolder.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewHolder.kt index 700d388081..3d9b0709af 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewHolder.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewHolder.kt @@ -1,19 +1,12 @@ package com.squareup.workflow1.ui import android.view.View -import com.squareup.workflow1.ui.ScreenViewHolder.Companion.Showing -import com.squareup.workflow1.ui.ScreenViewHolder.Companion.ShowingNothing /** * Associates a [view] with a function ([runner]) that can update it to display instances * of [ScreenT]. Also holds a reference to the [ViewEnvironment][environment] that was * most recently used to update the [view]. * - * [environment] should always hold a reference to the [Screen] most recently shown - * in [view], with the key [Showing]. [ScreenViewHolder.showing] provides easy access - * to it. Note that the shown [Screen] may not be of type [ScreenT], if this - * [ScreenViewHolder] is wrapped by another one. (See [ScreenViewFactory.forWrapper].) - * * Do not call [runner] directly. Use [ScreenViewHolder.show] instead. Or most commonly, * allow [WorkflowViewStub.show] to call it for you. */ @@ -29,32 +22,10 @@ public interface ScreenViewHolder { * The function that is run by [show] to update [view] with a new [Screen] rendering and * [ViewEnvironment]. * - * Prefer calling [show] to using this directly, to ensure that [Showing] is + * Prefer calling [show] to using this directly, to ensure that [screenOrNull] is * maintained correctly, and [showing] keeps working. */ public val runner: ScreenViewRunner - - public companion object { - /** - * Default value returned for the [Showing] [ViewEnvironmentKey], and therefore the - * default value returned by the [showing] method. Indicates that [show] has not yet - * been called, during the window between a [ScreenViewHolder] being instantiated, - * and the first call to [show]. - */ - public object ShowingNothing : Screen - - /** - * Provides access to the [Screen] instance most recently shown in a [ScreenViewHolder]'s - * [view] via [show], or through the legacy [View.showRendering] function. - * Call [showing] for more convenient access. - * - * If the host [View] is driven by a non-[Screen] rendering via the legacy - * [View.showRendering] function, it will be returned in an [AsScreen] wrapper. - */ - public object Showing : ViewEnvironmentKey(Screen::class) { - override val default: Screen = ShowingNothing - } - } } /** @@ -78,19 +49,18 @@ public fun interface ScreenViewRunner { /** * Returns true if [screen] is [compatible] with the [Screen] instance that - * was last [shown][show] by the [view] managed by the receiver. + * was last [shown][show] by the [view][ScreenViewHolder.view] managed by the receiver. */ @WorkflowUiExperimentalApi public fun ScreenViewHolder<*>.canShow(screen: Screen): Boolean { - // The ShowingNothing case covers bootstrapping, during the first call to show() + // The null case covers bootstrapping, during the first call to show() // from ScreenViewFactory.start(). - return showing.let { it is ShowingNothing || compatible(it, screen) } + return view.screenOrNull?.let { compatible(it, screen) } ?: true } /** * Updates the [view][ScreenViewHolder.view] managed by the receiver to * display [screen], and updates the receiver's [environment] as well. - * The new [environment] will hold a reference to [screen] with key [Showing]. */ @WorkflowUiExperimentalApi public fun ScreenViewHolder.show( @@ -99,8 +69,9 @@ public fun ScreenViewHolder.show( ) { // Why is this an extension rather than part of the interface? // When wrapping, we need to prevent recursive calls from clobbering - // `environment[Showing]` with the nested rendering type. - runner.showRendering(screen, environment + (Showing to screen)) + // `screenOrNull` with the nested rendering type. + view.screen = screen + runner.showRendering(screen, environment) } /** @@ -113,7 +84,7 @@ public fun ScreenViewHolder.show( */ @WorkflowUiExperimentalApi public val ScreenViewHolder<*>.showing: Screen - get() = environment[Showing] + get() = view.screen @WorkflowUiExperimentalApi public fun ScreenViewHolder( diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewFactory.kt index c24e6ec370..4a0810d8b8 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewFactory.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewFactory.kt @@ -5,8 +5,6 @@ import android.view.View import android.view.ViewGroup /** - * **This will be deprecated in favor of [ScreenViewFactory] very soon.** - * * Factory for [View] instances that can show renderings of type[RenderingT]. * * Two concrete [ViewFactory] implementations are provided: diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewShowRendering.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewShowRendering.kt index 01d308feb2..a6220de3b3 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewShowRendering.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewShowRendering.kt @@ -1,7 +1,6 @@ package com.squareup.workflow1.ui import android.view.View -import com.squareup.workflow1.ui.ScreenViewHolder.Companion.Showing import com.squareup.workflow1.ui.WorkflowViewState.New import com.squareup.workflow1.ui.WorkflowViewState.Started @@ -16,8 +15,6 @@ public typealias ViewShowRendering = // declare variance on a typealias. If I recall correctly. /** - * **This will be deprecated in favor of [ScreenViewHolder] very soon.** - * * For use by implementations of [ViewFactory.buildView]. Establishes [showRendering] * as the implementation of [View.showRendering] for the receiver, possibly replacing * the existing one. @@ -54,8 +51,6 @@ public fun View.bindShowRendering( } /** - * **This will be deprecated in favor of [ScreenViewFactory.startShowing] very soon.** - * * Note that [WorkflowViewStub] calls this method for you. * * Makes the initial call to [View.showRendering], along with any wrappers that have been @@ -73,8 +68,6 @@ public fun View.start() { } /** - * **This will be deprecated in favor of [ScreenViewHolder.canShow] very soon.** - * * Note that [WorkflowViewStub.showRendering] makes this check for you. * * True if this view is able to show [rendering]. @@ -91,8 +84,6 @@ public fun View.canShowRendering(rendering: Any): Boolean { } /** - * **This will be deprecated in favor of [ScreenViewHolder.show] very soon.** - * * It is usually more convenient to call [WorkflowViewStub.showRendering] * than to call this method directly. * @@ -116,9 +107,9 @@ public fun View.showRendering( // Update the tag's rendering and viewEnvironment before calling // the actual showRendering function. Note that we update both the - // new Showing key and the old workflowViewState backing View.getRendering(). - val updatedEnv = viewEnvironment + (Showing to asScreen(rendering)) - workflowViewState = Started(rendering, updatedEnv, viewState.showRendering) + // new showing tag and the old workflowViewState backing View.getRendering(). + screen = asScreen(rendering) + workflowViewState = Started(rendering, viewEnvironment, viewState.showRendering) viewState.showRendering.invoke(rendering, viewEnvironment) } @@ -130,8 +121,7 @@ public fun View.showRendering( * * Note that this is tied strictly to calls to [showRendering], and does not reflect * calls to [ScreenViewHolder.show], which is poised to replace that function. For - * reliable access to the latest rendering displayed by a View, use the [Showing] - * [ViewEnvironmentKey]. + * reliable access to the latest rendering displayed by a View, use the [screenOrNull]. * * @throws ClassCastException if the current rendering is not of type [RenderingT] */ @@ -154,15 +144,28 @@ public inline fun View.getRendering(): RenderingT? { /** * Returns the most recent [Screen] rendering [shown][ScreenViewHolder.show] in this view, - * or `null` if the receiver was not created via [ScreenViewFactory.startShowing]. + * or throws a [NullPointerException] if the receiver was not created via + * [ScreenViewFactory.startShowing]. If the receiver is showing non-[Screen] + * rendering set by the legacy [showRendering], it will be returned wrapped in [AsScreen]. + */ +@WorkflowUiExperimentalApi +public var View.screen: Screen + get() = checkNotNull(screenOrNull) { + "Expected to find a Screen in tag workflow_screen (${R.id.workflow_screen})" + } + internal set(value) = setTag(R.id.workflow_screen, value) + +/** + * Returns the most recent [Screen] rendering [shown][ScreenViewHolder.show] in this view, + * or `null` if the receiver was not created via [ScreenViewFactory.startShowing]. If + * the receiver is showing non-[Screen] rendering set by the legacy [showRendering], + * it will be returned wrapped in [AsScreen]. */ @WorkflowUiExperimentalApi public val View.screenOrNull: Screen? - get() = environmentOrNull?.get(Showing) + get() = getTag(R.id.workflow_screen) as? Screen /** - * **This will be deprecated in favor of [environmentOrNull] very soon.** - * * Returns the most recent [ViewEnvironment] applied to this view, or null if [bindShowRendering] * has never been called. */ @@ -184,8 +187,6 @@ public val View.environmentOrNull: ViewEnvironment? ?: getTag(R.id.workflow_environment) as? ViewEnvironment /** - * **This will be deprecated in favor of [ScreenViewHolder] very soon.** - * * Returns the function set by the most recent call to [bindShowRendering], or null * if that method has never been called. */ diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BackStackContainer.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BackStackContainer.kt index d3ba0f948c..72901c7fe7 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BackStackContainer.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BackStackContainer.kt @@ -20,7 +20,6 @@ import com.squareup.workflow1.ui.Compatible.Companion.keyFor import com.squareup.workflow1.ui.NamedScreen import com.squareup.workflow1.ui.R import com.squareup.workflow1.ui.ScreenViewHolder -import com.squareup.workflow1.ui.ScreenViewHolder.Companion.Showing import com.squareup.workflow1.ui.ViewEnvironment import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.stateRegistryOwnerFromViewTreeOrContext @@ -29,6 +28,7 @@ import com.squareup.workflow1.ui.canShow import com.squareup.workflow1.ui.compatible import com.squareup.workflow1.ui.container.BackStackConfig.First import com.squareup.workflow1.ui.container.BackStackConfig.Other +import com.squareup.workflow1.ui.screen import com.squareup.workflow1.ui.show import com.squareup.workflow1.ui.startShowing import com.squareup.workflow1.ui.toViewFactory @@ -66,7 +66,7 @@ public open class BackStackContainer @JvmOverloads constructor( newRendering: BackStackScreen<*>, newViewEnvironment: ViewEnvironment ) { - savedStateParentKey = keyFor(newViewEnvironment[Showing]) + savedStateParentKey = keyFor(screen) val config = if (newRendering.backStack.isEmpty()) First else Other val environment = newViewEnvironment + config diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BodyAndOverlaysContainer.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BodyAndOverlaysContainer.kt index d878da3e37..5308f8464c 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BodyAndOverlaysContainer.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BodyAndOverlaysContainer.kt @@ -14,10 +14,10 @@ import com.squareup.workflow1.ui.Compatible import com.squareup.workflow1.ui.R import com.squareup.workflow1.ui.ScreenViewFactory import com.squareup.workflow1.ui.ScreenViewHolder -import com.squareup.workflow1.ui.ScreenViewHolder.Companion.Showing import com.squareup.workflow1.ui.ViewEnvironment import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.WorkflowViewStub +import com.squareup.workflow1.ui.screen /** * Default container for [Overlay] renderings, providing support for @@ -56,7 +56,7 @@ internal class BodyAndOverlaysContainer @JvmOverloads constructor( newScreen: BodyAndOverlaysScreen<*, *>, viewEnvironment: ViewEnvironment ) { - savedStateParentKey = Compatible.keyFor(viewEnvironment[Showing]) + savedStateParentKey = Compatible.keyFor(screen) dialogs.update(newScreen.overlays, viewEnvironment) { env -> baseViewStub.show(newScreen.body, env) diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogOverlay.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogOverlay.kt new file mode 100644 index 0000000000..5fce44fd2b --- /dev/null +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogOverlay.kt @@ -0,0 +1,36 @@ +package com.squareup.workflow1.ui.container + +import android.app.Dialog +import android.view.View +import com.squareup.workflow1.ui.R +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi + +/** + * Returns the most recent [Overlay] rendering [shown][OverlayDialogHolder.show] in this [Dialog], + * or throws a [NullPointerException] ` if the receiver was not created via + * [OverlayDialogFactory.buildDialog]. + */ +@WorkflowUiExperimentalApi +public var Dialog.overlay: Overlay + get() = checkNotNull(overlayOrNull) { + "Expected to find an Overlay in tag workflow_overlay (${R.id.workflow_overlay}) " + + "on the decor view of $this" + } + internal set(value) = decorView.setTag(R.id.workflow_overlay, value) + +/** + * Returns the most recent [Overlay] rendering [shown][OverlayDialogHolder.show] in this [Dialog], + * or `null` if the receiver was not created via [OverlayDialogFactory.buildDialog]. + */ +@WorkflowUiExperimentalApi +public val Dialog.overlayOrNull: Overlay? + get() = decorViewOrNull?.getTag(R.id.workflow_overlay) as? Overlay + +internal val Dialog.decorView: View + get() { + val window = checkNotNull(window) { "Expected to find a window on $this" } + return window.decorView + } + +internal val Dialog.decorViewOrNull: View? + get() = window?.peekDecorView() diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogSession.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogSession.kt index a1c350f441..f60afe4f90 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogSession.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogSession.kt @@ -27,6 +27,7 @@ import com.squareup.workflow1.ui.androidx.WorkflowSavedStateRegistryAggregator @WorkflowUiExperimentalApi internal class DialogSession( index: Int, + initialOverlay: Overlay, holder: OverlayDialogHolder ) { // Note similar code in LayeredDialogSessions @@ -55,7 +56,7 @@ internal class DialogSession( holder.show(overlay, environment) } - val savedStateRegistryKey = Compatible.keyFor(holder.showing, index.toString()) + val savedStateRegistryKey = Compatible.keyFor(initialOverlay, index.toString()) private val KeyEvent.isBackPress: Boolean get() = (keyCode == KEYCODE_BACK || keyCode == KEYCODE_ESCAPE) && action == ACTION_UP @@ -91,7 +92,7 @@ internal class DialogSession( } dialog.show() - dialog.window?.decorView?.also { decorView -> + dialog.decorView.also { decorView -> // Implementations of buildDialog may set their own WorkflowLifecycleOwner on the // content view, so to avoid interfering with them we also set it here. When the views // are attached, this will become the parent lifecycle of the one from buildDialog if @@ -142,11 +143,11 @@ internal class DialogSession( internal fun save(): KeyAndBundle? { val saved = holder.dialog.window?.saveHierarchyState() ?: return null - return KeyAndBundle(Compatible.keyFor(holder.showing), saved) + return KeyAndBundle(savedStateRegistryKey, saved) } internal fun restore(keyAndBundle: KeyAndBundle) { - if (Compatible.keyFor(holder.showing) == keyAndBundle.compatibilityKey) { + if (savedStateRegistryKey == keyAndBundle.compatibilityKey) { holder.dialog.window?.restoreHierarchyState(keyAndBundle.bundle) } } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/LayeredDialogSessions.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/LayeredDialogSessions.kt index 6ee35ac0f7..988c5bae07 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/LayeredDialogSessions.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/LayeredDialogSessions.kt @@ -18,7 +18,6 @@ import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport import com.squareup.workflow1.ui.androidx.WorkflowLifecycleOwner import com.squareup.workflow1.ui.androidx.WorkflowSavedStateRegistryAggregator import com.squareup.workflow1.ui.container.DialogSession.KeyAndBundle -import com.squareup.workflow1.ui.container.OverlayDialogHolder.Companion.InOverlay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -142,13 +141,7 @@ public class LayeredDialogSessions private constructor( for ((i, overlay) in overlays.withIndex()) { val covered = i < modalIndex - // Seed InOverlay before the Dialog is created, so that it's available to - // DialogSession before the first call to OverlayDialogHolder.show (which is - // what normally sets it). - // https://github.com/square/workflow-kotlin/issues/825 - val envPlusInOverlay = envPlusBounds + (InOverlay to overlay) - val dialogEnv = - if (covered) envPlusInOverlay + (CoveredByModal to true) else envPlusInOverlay + val dialogEnv = if (covered) envPlusBounds + (CoveredByModal to true) else envPlusBounds updatedSessions += if (i < sessions.size && sessions[i].holder.canShow(overlay)) { // There is already a dialog at this index, and it is compatible @@ -162,7 +155,7 @@ public class LayeredDialogSessions private constructor( holder.dialog.maintainBounds(holder.environment) { b -> updateBounds(b) } } - DialogSession(i, holder).also { newSession -> + DialogSession(i, overlay, holder).also { newSession -> // Prime the pump, make the first call to OverlayDialog.show to update // the new dialog to reflect the first rendering. newSession.holder.show(overlay, dialogEnv) diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/OverlayDialogHolder.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/OverlayDialogHolder.kt index b5ba2fb978..557246b61b 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/OverlayDialogHolder.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/OverlayDialogHolder.kt @@ -4,11 +4,8 @@ import android.app.Dialog import android.graphics.Rect import com.squareup.workflow1.ui.Screen import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.ViewEnvironmentKey import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.compatible -import com.squareup.workflow1.ui.container.OverlayDialogHolder.Companion.InOverlay -import com.squareup.workflow1.ui.container.OverlayDialogHolder.Companion.NoOverlay import com.squareup.workflow1.ui.onBackPressedDispatcherOwnerOrNull import com.squareup.workflow1.ui.show @@ -29,7 +26,7 @@ public interface OverlayDialogHolder { * The function that is run by [show] to update [dialog] with a new [Screen] rendering and * [ViewEnvironment]. * - * Prefer calling [show] to using this directly, to ensure that [InOverlay] is + * Prefer calling [show] to using this directly, to ensure that [overlayOrNull] is * maintained correctly, and [showing] keeps working. */ public val runner: (rendering: OverlayT, environment: ViewEnvironment) -> Unit @@ -58,24 +55,6 @@ public interface OverlayDialogHolder { * method. */ public val onBackPressed: (() -> Unit)? - - public companion object { - /** - * Default value returned for the [InOverlay] [ViewEnvironmentKey], and therefore the - * default value returned by the [showing] method. Indicates that [show] has not yet - * been called, during the window between a [OverlayDialogHolder] being instantiated, - * and the first call to [show]. - */ - public object NoOverlay : Overlay - - /** - * Provides access to the [Overlay] instance most recently shown in an - * [OverlayDialogHolder]'s [dialog] via [show]. Call [showing] for more convenient access. - */ - public object InOverlay : ViewEnvironmentKey(Overlay::class) { - override val default: Overlay = NoOverlay - } - } } /** @@ -84,15 +63,13 @@ public interface OverlayDialogHolder { */ @WorkflowUiExperimentalApi public fun OverlayDialogHolder<*>.canShow(overlay: Overlay): Boolean { - // The ShowingNothing case covers bootstrapping, during the first call to show() - // from OverlayDialogFactory.start(). - return showing.let { it is NoOverlay || compatible(it, overlay) } + // The null case covers bootstrapping, during the first call to show(). + return dialog.overlayOrNull?.let { compatible(it, overlay) } ?: true } /** * Updates the [dialog][OverlayDialogHolder.dialog] managed by the receiver to * display [overlay], and updates the receiver's [environment] as well. - * The new [environment] will hold a reference to [overlay] with key [InOverlay]. */ @WorkflowUiExperimentalApi public fun OverlayDialogHolder.show( @@ -101,8 +78,9 @@ public fun OverlayDialogHolder.show( ) { // Why is this an extension rather than part of the interface? // When wrapping, we need to prevent recursive calls from clobbering - // `environment[InOverlay]` with the nested rendering type. - runner(overlay, environment + (InOverlay to overlay)) + // `overlayOrNull` with the nested rendering type. + dialog.overlay = overlay + runner(overlay, environment) } /** @@ -114,7 +92,7 @@ public fun OverlayDialogHolder.show( */ @WorkflowUiExperimentalApi public val OverlayDialogHolder<*>.showing: Overlay - get() = environment[InOverlay] + get() = dialog.overlay @WorkflowUiExperimentalApi public fun OverlayDialogHolder( diff --git a/workflow-ui/core-android/src/main/res/values/ids.xml b/workflow-ui/core-android/src/main/res/values/ids.xml index 9fc68a21c7..d1100790a8 100644 --- a/workflow-ui/core-android/src/main/res/values/ids.xml +++ b/workflow-ui/core-android/src/main/res/values/ids.xml @@ -25,4 +25,8 @@ + + + + diff --git a/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/LegacyAndroidViewRegistryTest.kt b/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/LegacyAndroidViewRegistryTest.kt index 55f6921ef1..dbd6f36278 100644 --- a/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/LegacyAndroidViewRegistryTest.kt +++ b/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/LegacyAndroidViewRegistryTest.kt @@ -6,7 +6,6 @@ import android.content.Context import android.view.View import android.view.ViewGroup import com.google.common.truth.Truth.assertThat -import com.squareup.workflow1.ui.ScreenViewHolder.Companion.Showing import com.squareup.workflow1.ui.ViewRegistry.Entry import com.squareup.workflow1.ui.container.mockView import org.junit.Test @@ -126,21 +125,21 @@ internal class LegacyAndroidViewRegistryTest { assertThat(overrideViewRenderingFactory.called).isTrue() } - @Test fun `buildView auto converts unwrapped Screen and updates Showing correctly`() { + @Test fun `buildView auto converts unwrapped Screen and updates screenOrNull correctly`() { val registry = ViewRegistry() val view = registry.buildView(ScreenRendering) view.start() assertThat(view.getRendering()).isSameInstanceAs(ScreenRendering) - assertThat(view.environmentOrNull!![Showing]).isSameInstanceAs(ScreenRendering) + assertThat(view.screenOrNull).isSameInstanceAs(ScreenRendering) } - @Test fun `buildView auto converts wrapped Screen and updates Showing correctly`() { + @Test fun `buildView auto converts wrapped Screen and updates screen correctly`() { val registry = ViewRegistry() val rendering = Named(ScreenRendering, "fnord") val view = registry.buildView(rendering) view.start() assertThat(compatible(view.getRendering()!!, rendering)).isTrue() - assertThat(compatible(view.environmentOrNull!![Showing], asScreen(rendering))).isTrue() + assertThat(compatible(view.screen, asScreen(rendering))).isTrue() } @Test fun `ViewRegistry with no arguments infers type`() { diff --git a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/Named.kt b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/Named.kt index d462a0f6c6..85db722c19 100644 --- a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/Named.kt +++ b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/Named.kt @@ -1,8 +1,6 @@ package com.squareup.workflow1.ui /** - * **This will be deprecated in favor of [NamedScreen] very soon.** - * * Allows renderings that do not implement [Compatible] themselves to be distinguished * by more than just their type. Instances are [compatible] if they have the same name * and have [compatible] [wrapped] fields.