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

Replaces *.initializeView with *.viewStarter #602

Merged
merged 2 commits into from
Dec 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.squareup.workflow1.ui.WorkflowViewStub
import com.squareup.workflow1.ui.getFactoryForRendering
import com.squareup.workflow1.ui.getShowRendering
import com.squareup.workflow1.ui.showRendering
import com.squareup.workflow1.ui.start
import kotlin.reflect.KClass

/**
Expand Down Expand Up @@ -184,6 +185,8 @@ private fun <R : Any> ViewFactory<R>.asComposeViewFactory() =
// we don't have access to that.
originalFactory.buildView(rendering, viewEnvironment, context, container = null)
.also { view ->
view.start()

// Mirrors the check done in ViewRegistry.buildView.
checkNotNull(view.getShowRendering<Any>()) {
"View.bindShowRendering should have been called for $view, typically by the " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ import com.squareup.workflow1.ui.canShowRendering
import com.squareup.workflow1.ui.compatible
import com.squareup.workflow1.ui.container.R
import com.squareup.workflow1.ui.getRendering
import com.squareup.workflow1.ui.showFirstRendering
import com.squareup.workflow1.ui.showRendering
import com.squareup.workflow1.ui.start

/**
* A container view that can display a stream of [BackStackScreen] instances.
Expand Down Expand Up @@ -103,11 +103,12 @@ public open class BackStackContainer @JvmOverloads constructor(
initialViewEnvironment = environment,
contextForNewView = this.context,
container = this,
initializeView = {
WorkflowLifecycleOwner.installOn(this)
showFirstRendering()
viewStarter = { view, doStart ->
WorkflowLifecycleOwner.installOn(view)
doStart()
}
)
newView.start()
viewStateCache.update(named.backStack, oldViewMaybe, newView)

val popped = currentRendering?.backStack?.any { compatible(it, named.top) } == true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.squareup.workflow1.ui.buildView
import com.squareup.workflow1.ui.modal.ModalViewContainer.Companion.binding
import com.squareup.workflow1.ui.onBackPressedDispatcherOwnerOrNull
import com.squareup.workflow1.ui.showRendering
import com.squareup.workflow1.ui.start
import kotlin.reflect.KClass

/**
Expand Down Expand Up @@ -72,6 +73,7 @@ public open class ModalViewContainer @JvmOverloads constructor(
container = this
)
.apply {
start()
// If the modal's root view has no backPressedHandler, add a no-op one to
// ensure that the `onBackPressed` call below will not leak up to handlers
// that should be blocked by this modal session.
Expand Down
70 changes: 47 additions & 23 deletions workflow-ui/core-android/api/core-android.api
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ public final class com/squareup/workflow1/ui/BuilderViewFactory : com/squareup/w
}

public final class com/squareup/workflow1/ui/DecorativeViewFactory : com/squareup/workflow1/ui/ViewFactory {
public fun <init> (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;)V
public synthetic fun <init> (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;)V
public synthetic fun <init> (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/ui/ViewStarter;Lkotlin/jvm/functions/Function4;)V
public synthetic fun <init> (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/ui/ViewStarter;Lkotlin/jvm/functions/Function4;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function2;Lcom/squareup/workflow1/ui/ViewStarter;Lkotlin/jvm/functions/Function4;)V
public synthetic fun <init> (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function2;Lcom/squareup/workflow1/ui/ViewStarter;Lkotlin/jvm/functions/Function4;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun buildView (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View;
public fun getType ()Lkotlin/reflect/KClass;
}
Expand All @@ -51,21 +51,6 @@ public final class com/squareup/workflow1/ui/LayoutRunnerViewFactory : com/squar
public fun getType ()Lkotlin/reflect/KClass;
}

public final class com/squareup/workflow1/ui/ShowRenderingTag {
public fun <init> (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;)V
public final fun component1 ()Ljava/lang/Object;
public final fun component2 ()Lcom/squareup/workflow1/ui/ViewEnvironment;
public final fun component3 ()Lkotlin/jvm/functions/Function2;
public final fun copy (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/ShowRenderingTag;
public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/ShowRenderingTag;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/ShowRenderingTag;
public fun equals (Ljava/lang/Object;)Z
public final fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment;
public final fun getShowRendering ()Lkotlin/jvm/functions/Function2;
public final fun getShowing ()Ljava/lang/Object;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/squareup/workflow1/ui/SnapshotParcelsKt {
public static final fun toSnapshot (Landroid/os/Parcelable;)Lcom/squareup/workflow1/Snapshot;
}
Expand Down Expand Up @@ -124,21 +109,24 @@ public final class com/squareup/workflow1/ui/ViewRegistry$Companion : com/square
public final class com/squareup/workflow1/ui/ViewRegistryKt {
public static final fun ViewRegistry ()Lcom/squareup/workflow1/ui/ViewRegistry;
public static final fun ViewRegistry ([Lcom/squareup/workflow1/ui/ViewFactory;)Lcom/squareup/workflow1/ui/ViewRegistry;
public static final fun buildView (Lcom/squareup/workflow1/ui/ViewRegistry;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;Lkotlin/jvm/functions/Function1;)Landroid/view/View;
public static synthetic fun buildView$default (Lcom/squareup/workflow1/ui/ViewRegistry;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Landroid/view/View;
public static final fun buildView (Lcom/squareup/workflow1/ui/ViewRegistry;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;Lcom/squareup/workflow1/ui/ViewStarter;)Landroid/view/View;
public static synthetic fun buildView$default (Lcom/squareup/workflow1/ui/ViewRegistry;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;Lcom/squareup/workflow1/ui/ViewStarter;ILjava/lang/Object;)Landroid/view/View;
public static final fun getFactoryForRendering (Lcom/squareup/workflow1/ui/ViewRegistry;Ljava/lang/Object;)Lcom/squareup/workflow1/ui/ViewFactory;
public static final fun plus (Lcom/squareup/workflow1/ui/ViewRegistry;Lcom/squareup/workflow1/ui/ViewFactory;)Lcom/squareup/workflow1/ui/ViewRegistry;
public static final fun plus (Lcom/squareup/workflow1/ui/ViewRegistry;Lcom/squareup/workflow1/ui/ViewRegistry;)Lcom/squareup/workflow1/ui/ViewRegistry;
public static final fun showFirstRendering (Landroid/view/View;)V
}

public final class com/squareup/workflow1/ui/ViewShowRenderingKt {
public static final fun bindShowRendering (Landroid/view/View;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;)V
public static final fun canShowRendering (Landroid/view/View;Ljava/lang/Object;)Z
public static final fun getEnvironment (Landroid/view/View;)Lcom/squareup/workflow1/ui/ViewEnvironment;
public static final fun getRendering (Landroid/view/View;)Ljava/lang/Object;
public static final fun getShowRendering (Landroid/view/View;)Lkotlin/jvm/functions/Function2;
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
}

public abstract interface class com/squareup/workflow1/ui/ViewStarter {
public abstract fun startView (Landroid/view/View;Lkotlin/jvm/functions/Function0;)V
}

public final class com/squareup/workflow1/ui/WorkflowLayout : android/widget/FrameLayout {
Expand All @@ -149,6 +137,42 @@ public final class com/squareup/workflow1/ui/WorkflowLayout : android/widget/Fra
public static synthetic fun start$default (Lcom/squareup/workflow1/ui/WorkflowLayout;Lkotlinx/coroutines/flow/Flow;Lcom/squareup/workflow1/ui/ViewEnvironment;ILjava/lang/Object;)V
}

public abstract class com/squareup/workflow1/ui/WorkflowViewState {
public abstract fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment;
public abstract fun getShowRendering ()Lkotlin/jvm/functions/Function2;
}

public final class com/squareup/workflow1/ui/WorkflowViewState$New : com/squareup/workflow1/ui/WorkflowViewState {
public fun <init> (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V
public synthetic fun <init> (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component2 ()Lcom/squareup/workflow1/ui/ViewEnvironment;
public final fun component3 ()Lkotlin/jvm/functions/Function2;
public final fun component4 ()Lkotlin/jvm/functions/Function1;
public final fun copy (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/WorkflowViewState$New;
public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/WorkflowViewState$New;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/WorkflowViewState$New;
public fun equals (Ljava/lang/Object;)Z
public fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment;
public fun getShowRendering ()Lkotlin/jvm/functions/Function2;
public synthetic fun getShowing ()Ljava/lang/Object;
public final fun getStarter ()Lkotlin/jvm/functions/Function1;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/squareup/workflow1/ui/WorkflowViewState$Started : com/squareup/workflow1/ui/WorkflowViewState {
public fun <init> (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;)V
public final fun component2 ()Lcom/squareup/workflow1/ui/ViewEnvironment;
public final fun component3 ()Lkotlin/jvm/functions/Function2;
public final fun copy (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/WorkflowViewState$Started;
public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/WorkflowViewState$Started;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/WorkflowViewState$Started;
public fun equals (Ljava/lang/Object;)Z
public fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment;
public fun getShowRendering ()Lkotlin/jvm/functions/Function2;
public synthetic fun getShowing ()Ljava/lang/Object;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/squareup/workflow1/ui/WorkflowViewStub : android/view/View {
public fun <init> (Landroid/content/Context;)V
public fun <init> (Landroid/content/Context;Landroid/util/AttributeSet;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.junit.Test
internal class DecorativeViewFactoryTest {
private val instrumentation = InstrumentationRegistry.getInstrumentation()

@Test fun initializeView_is_only_call_to_showRendering() {
@Test fun viewStarter_is_only_call_to_showRendering() {
val events = mutableListOf<String>()

val innerViewFactory = object : ViewFactory<InnerRendering> {
Expand All @@ -38,27 +38,26 @@ internal class DecorativeViewFactoryTest {
val enhancedEnv = env + (envString to "Updated environment")
Pair(outer.wrapped, enhancedEnv)
},
initializeView = {
val outerRendering = getRendering<OuterRendering>()
events += "initializeView $outerRendering ${environment!![envString]}"
showFirstRendering()
events += "exit initializeView"
viewStarter = { view, doStart ->
events += "viewStarter ${view.getRendering<Any>()} ${view.environment!![envString]}"
doStart()
events += "exit viewStarter"
}
)
val viewRegistry = ViewRegistry(innerViewFactory)
val viewRegistry = ViewRegistry(innerViewFactory, outerViewFactory)
val viewEnvironment = ViewEnvironment(mapOf(ViewRegistry to viewRegistry))

outerViewFactory.buildView(
viewRegistry.buildView(
OuterRendering("outer", InnerRendering("inner")),
viewEnvironment,
instrumentation.context
)
).start()

assertThat(events).containsExactly(
"initializeView OuterRendering(outerData=outer, wrapped=InnerRendering(innerData=inner)) " +
"viewStarter OuterRendering(outerData=outer, wrapped=InnerRendering(innerData=inner)) " +
"Updated environment",
"inner showRendering InnerRendering(innerData=inner)",
"exit initializeView"
"exit viewStarter"
)
}

Expand Down Expand Up @@ -86,21 +85,99 @@ internal class DecorativeViewFactoryTest {
innerShowRendering(outerRendering.wrapped, env)
}
)
val viewRegistry = ViewRegistry(innerViewFactory)
val viewRegistry = ViewRegistry(innerViewFactory, outerViewFactory)
val viewEnvironment = ViewEnvironment(mapOf(ViewRegistry to viewRegistry))

outerViewFactory.buildView(
viewRegistry.buildView(
OuterRendering("outer", InnerRendering("inner")),
viewEnvironment,
instrumentation.context
)
).start()

assertThat(events).containsExactly(
"doShowRendering OuterRendering(outerData=outer, wrapped=InnerRendering(innerData=inner))",
"inner showRendering InnerRendering(innerData=inner)"
)
}

// https://github.com/square/workflow-kotlin/issues/597
@Test fun double_wrapping_only_calls_showRendering_once() {
val events = mutableListOf<String>()

val innerViewFactory = object : ViewFactory<InnerRendering> {
override val type = InnerRendering::class
override fun buildView(
initialRendering: InnerRendering,
initialViewEnvironment: ViewEnvironment,
contextForNewView: Context,
container: ViewGroup?
): View = InnerView(contextForNewView).apply {
bindShowRendering(initialRendering, initialViewEnvironment) { rendering, _ ->
events += "inner showRendering $rendering"
}
}
}

val envString = object : ViewEnvironmentKey<String>(String::class) {
override val default: String get() = "Not set"
}

val outerViewFactory = DecorativeViewFactory(
type = OuterRendering::class,
map = { outer, env ->
val enhancedEnv = env + (envString to "Outer Updated environment" +
" SHOULD NOT SEE THIS! It will be clobbered by WayOutRendering")
Pair(outer.wrapped, enhancedEnv)
},
viewStarter = { view, doStart ->
events += "outer viewStarter ${view.getRendering<Any>()} ${view.environment!![envString]}"
doStart()
events += "exit outer viewStarter"
}
)

val wayOutViewFactory = DecorativeViewFactory(
type = WayOutRendering::class,
map = { wayOut, env ->
val enhancedEnv = env + (envString to "Way Out Updated environment triumphs over all")
Pair(wayOut.wrapped, enhancedEnv)
},
viewStarter = { view, doStart ->
events += "way out viewStarter ${view.getRendering<Any>()} ${view.environment!![envString]}"
doStart()
events += "exit way out viewStarter"
}
)
val viewRegistry = ViewRegistry(innerViewFactory, outerViewFactory, wayOutViewFactory)
val viewEnvironment = ViewEnvironment(mapOf(ViewRegistry to viewRegistry))

viewRegistry.buildView(
WayOutRendering("way out", OuterRendering("outer", InnerRendering("inner"))),
viewEnvironment,
instrumentation.context
).start()

assertThat(events).containsExactly(
"way out viewStarter " +
"WayOutRendering(wayOutData=way out, wrapped=" +
"OuterRendering(outerData=outer, wrapped=" +
"InnerRendering(innerData=inner))) " +
"Way Out Updated environment triumphs over all",
"outer viewStarter " +
// Notice that both the initial rendering and the ViewEnvironment are stomped by
// the outermost wrapper before inners are invoked. Could try to give
// the inner wrapper access to the rendering it expected, but there are no
// use cases and it trashes the API.
"WayOutRendering(wayOutData=way out, wrapped=" +
"OuterRendering(outerData=outer, wrapped=" +
"InnerRendering(innerData=inner))) " +
"Way Out Updated environment triumphs over all",
"inner showRendering InnerRendering(innerData=inner)",
"exit outer viewStarter",
"exit way out viewStarter"
)
}

@Test fun subsequent_showRendering_calls_wrapped_showRendering() {
val events = mutableListOf<String>()

Expand All @@ -125,14 +202,14 @@ internal class DecorativeViewFactoryTest {
innerShowRendering(outerRendering.wrapped, env)
}
)
val viewRegistry = ViewRegistry(innerViewFactory)
val viewRegistry = ViewRegistry(innerViewFactory, outerViewFactory)
val viewEnvironment = ViewEnvironment(mapOf(ViewRegistry to viewRegistry))

val view = outerViewFactory.buildView(
val view = viewRegistry.buildView(
OuterRendering("out1", InnerRendering("in1")),
viewEnvironment,
instrumentation.context
)
).apply { start() }
events.clear()

view.showRendering(OuterRendering("out2", InnerRendering("in2")), viewEnvironment)
Expand All @@ -149,5 +226,10 @@ internal class DecorativeViewFactoryTest {
val wrapped: InnerRendering
)

private data class WayOutRendering(
val wayOutData: String,
val wrapped: OuterRendering
)

private class InnerView(context: Context) : View(context)
}