From 20babcac6ee838e47821c18bdf701683867db465 Mon Sep 17 00:00:00 2001 From: Zach Klippenstein Date: Fri, 27 Nov 2020 08:50:33 -0800 Subject: [PATCH] WIP Upgrade workflow to latest alpha. TODO: - Still getting an IR generation bug, but I think it's fixed in 1.4.20, so rebase after upgrading kotlin + compose. --- README.md | 4 +- build.gradle.kts | 1 + buildSrc/src/main/java/Dependencies.kt | 8 +- compose-tooling/api/compose-tooling.api | 2 +- .../tooling/PreviewComposeWorkflowTest.kt | 10 +- .../compose/tooling/PreviewViewFactoryTest.kt | 6 +- .../ui/compose/tooling/ComposeWorkflows.kt | 10 +- .../compose/tooling/PlaceholderViewFactory.kt | 4 +- .../compose/tooling/PreviewViewEnvironment.kt | 13 +- .../ui/compose/tooling/ViewFactories.kt | 8 +- core-compose/api/core-compose.api | 36 ++-- .../ui/compose/ComposeViewFactoryTest.kt | 6 +- .../workflow/ui/compose/RenderAsStateTest.kt | 22 +-- .../ui/compose/ViewEnvironmentsTest.kt | 4 +- .../ui/compose/WorkflowContainerTest.kt | 8 +- .../ui/compose/internal/ViewFactoriesTest.kt | 14 +- .../workflow/ui/compose/ComposeRendering.kt | 6 +- .../workflow/ui/compose/ComposeViewFactory.kt | 12 +- .../workflow/ui/compose/ComposeWorkflow.kt | 16 +- .../workflow/ui/compose/CompositionRoot.kt | 6 +- .../workflow/ui/compose/RenderAsState.kt | 167 ++++++++++-------- .../workflow/ui/compose/ViewEnvironments.kt | 30 +++- .../workflow/ui/compose/WorkflowContainer.kt | 77 ++++---- .../compose/internal/ComposeWorkflowImpl.kt | 13 +- .../ui/compose/internal/ParentComposition.kt | 6 +- .../ui/compose/internal/ViewFactories.kt | 17 +- .../ui/compose/internal/ViewRegistries.kt | 13 +- .../com/squareup/sample/hellocompose/App.kt | 13 +- .../sample/hellocompose/HelloWorkflow.kt | 33 ++-- .../hellocomposebinding/HelloBinding.kt | 2 +- .../HelloBindingActivity.kt | 22 ++- .../hellocomposebinding/HelloWorkflow.kt | 14 +- .../HelloComposeRenderingActivity.kt | 13 +- .../HelloRenderingWorkflow.kt | 9 +- .../hellocomposerendering/HelloWorkflow.kt | 13 +- .../sample/nestedrenderings/LegacyRunner.kt | 11 +- .../NestedRenderingsActivity.kt | 22 ++- .../nestedrenderings/RecursiveViewFactory.kt | 6 +- .../nestedrenderings/RecursiveWorkflow.kt | 17 +- .../java/com/squareup/sample/textinput/App.kt | 13 +- .../sample/textinput/TextInputViewFactory.kt | 2 +- .../sample/textinput/TextInputWorkflow.kt | 19 +- samples/src/main/res/layout/legacy_view.xml | 2 +- 43 files changed, 417 insertions(+), 313 deletions(-) diff --git a/README.md b/README.md index 5173043d..dc00008e 100644 --- a/README.md +++ b/README.md @@ -92,5 +92,5 @@ limitations under the License. ``` [1]: https://developer.android.com/jetpack/compose -[2]: https://square.github.io/workflow/kotlin/api/workflow/com.squareup.workflow.ui/-view-factory/ -[3]: https://square.github.io/workflow/kotlin/api/workflow/com.squareup.workflow.ui/-view-registry/ +[2]: https://square.github.io/workflow/kotlin/api/workflow/com.squareup.workflow1.ui/-view-factory/ +[3]: https://square.github.io/workflow/kotlin/api/workflow/com.squareup.workflow1.ui/-view-registry/ diff --git a/build.gradle.kts b/build.gradle.kts index 19d9f0bd..a8ab7428 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -50,6 +50,7 @@ subprojects { jcenter() // For Kotlin 1.4. maven("https://dl.bintray.com/kotlin/kotlin-eap") + mavenLocal() } apply(plugin = "org.jlleitschuh.gradle.ktlint") diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index d7675069..ede30687 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -25,7 +25,7 @@ object Versions { // This *is* actually used. @Suppress("unused") const val targetSdk = 29 - const val workflow = "0.28.0" + const val workflow = "1.0.0-alpha.8-SNAPSHOT" } @Suppress("unused") @@ -78,11 +78,11 @@ object Dependencies { } object Workflow { - const val core = "com.squareup.workflow:workflow-core-jvm:${Versions.workflow}" - const val runtime = "com.squareup.workflow:workflow-runtime-jvm:${Versions.workflow}" + const val core = "com.squareup.workflow1:workflow-core-jvm:${Versions.workflow}" + const val runtime = "com.squareup.workflow1:workflow-runtime-jvm:${Versions.workflow}" object UI { - const val coreAndroid = "com.squareup.workflow:workflow-ui-core-android:${Versions.workflow}" + const val coreAndroid = "com.squareup.workflow1:workflow-ui-core-android:${Versions.workflow}" } } } diff --git a/compose-tooling/api/compose-tooling.api b/compose-tooling/api/compose-tooling.api index 61791364..00c745d3 100644 --- a/compose-tooling/api/compose-tooling.api +++ b/compose-tooling/api/compose-tooling.api @@ -10,6 +10,6 @@ public final class com/squareup/workflow/ui/compose/tooling/ComposeWorkflowsKt { } public final class com/squareup/workflow/ui/compose/tooling/ViewFactoriesKt { - public static final fun preview (Lcom/squareup/workflow/ui/ViewFactory;Ljava/lang/Object;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V + public static final fun preview (Lcom/squareup/workflow1/ui/ViewFactory;Ljava/lang/Object;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V } diff --git a/compose-tooling/src/androidTest/java/com/squareup/workflow/ui/compose/tooling/PreviewComposeWorkflowTest.kt b/compose-tooling/src/androidTest/java/com/squareup/workflow/ui/compose/tooling/PreviewComposeWorkflowTest.kt index 1f86e5a9..b33ab714 100644 --- a/compose-tooling/src/androidTest/java/com/squareup/workflow/ui/compose/tooling/PreviewComposeWorkflowTest.kt +++ b/compose-tooling/src/androidTest/java/com/squareup/workflow/ui/compose/tooling/PreviewComposeWorkflowTest.kt @@ -29,16 +29,16 @@ import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.ui.test.createComposeRule import androidx.ui.tooling.preview.Preview -import com.squareup.workflow.Workflow -import com.squareup.workflow.ui.ViewEnvironmentKey -import com.squareup.workflow.ui.compose.WorkflowRendering -import com.squareup.workflow.ui.compose.composed +import com.squareup.workflow1.Workflow +import com.squareup.workflow1.ui.ViewEnvironmentKey +import com.squareup.workflow1.ui.compose.WorkflowRendering +import com.squareup.workflow1.ui.compose.composed import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith /** - * Duplicate of [PreviewViewFactoryTest] but for [com.squareup.workflow.ui.compose.ComposeWorkflow]. + * Duplicate of [PreviewViewFactoryTest] but for [com.squareup.workflow1.ui.compose.ComposeWorkflow]. */ @RunWith(AndroidJUnit4::class) class PreviewComposeWorkflowTest { diff --git a/compose-tooling/src/androidTest/java/com/squareup/workflow/ui/compose/tooling/PreviewViewFactoryTest.kt b/compose-tooling/src/androidTest/java/com/squareup/workflow/ui/compose/tooling/PreviewViewFactoryTest.kt index d39bb091..928a8834 100644 --- a/compose-tooling/src/androidTest/java/com/squareup/workflow/ui/compose/tooling/PreviewViewFactoryTest.kt +++ b/compose-tooling/src/androidTest/java/com/squareup/workflow/ui/compose/tooling/PreviewViewFactoryTest.kt @@ -29,9 +29,9 @@ import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.ui.test.createComposeRule import androidx.ui.tooling.preview.Preview -import com.squareup.workflow.ui.ViewEnvironmentKey -import com.squareup.workflow.ui.compose.WorkflowRendering -import com.squareup.workflow.ui.compose.composedViewFactory +import com.squareup.workflow1.ui.ViewEnvironmentKey +import com.squareup.workflow1.ui.compose.WorkflowRendering +import com.squareup.workflow1.ui.compose.composedViewFactory import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith diff --git a/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/ComposeWorkflows.kt b/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/ComposeWorkflows.kt index c13f2516..5908ac06 100644 --- a/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/ComposeWorkflows.kt +++ b/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/ComposeWorkflows.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) @file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry") package com.squareup.workflow.ui.compose.tooling @@ -20,11 +21,12 @@ package com.squareup.workflow.ui.compose.tooling import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.squareup.workflow.Sink -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewFactory -import com.squareup.workflow.ui.ViewRegistry import com.squareup.workflow.ui.compose.ComposeWorkflow +import com.squareup.workflow1.Sink +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** * Draws this [ComposeWorkflow] using a special preview [ViewRegistry]. diff --git a/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/PlaceholderViewFactory.kt b/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/PlaceholderViewFactory.kt index 7e623ac0..017f6900 100644 --- a/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/PlaceholderViewFactory.kt +++ b/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/PlaceholderViewFactory.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) @file:Suppress("SameParameterValue", "DEPRECATION") package com.squareup.workflow.ui.compose.tooling @@ -40,8 +41,9 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.ui.tooling.preview.Preview -import com.squareup.workflow.ui.ViewFactory import com.squareup.workflow.ui.compose.composedViewFactory +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** * A [ViewFactory] that will be used any time a [PreviewViewRegistry] is asked to show a rendering. diff --git a/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/PreviewViewEnvironment.kt b/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/PreviewViewEnvironment.kt index 388a0bc4..5d49e9b3 100644 --- a/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/PreviewViewEnvironment.kt +++ b/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/PreviewViewEnvironment.kt @@ -13,15 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.workflow.ui.compose.tooling import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewFactory -import com.squareup.workflow.ui.ViewRegistry +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import kotlin.reflect.KClass /** @@ -41,7 +44,7 @@ import kotlin.reflect.KClass PreviewViewRegistry(mainFactory, placeholderViewFactory(placeholderModifier)) } return remember(viewRegistry, viewEnvironmentUpdater) { - ViewEnvironment(viewRegistry).let { environment -> + ViewEnvironment(mapOf(ViewRegistry to viewRegistry)).let { environment -> // Give the preview a chance to add its own elements to the ViewEnvironment. viewEnvironmentUpdater?.let { it(environment) } ?: environment } @@ -50,7 +53,7 @@ import kotlin.reflect.KClass /** * A [ViewRegistry] that uses [mainFactory] for rendering [RenderingT]s, and [placeholderFactory] - * for all other [WorkflowRendering][com.squareup.workflow.ui.compose.WorkflowRendering] calls. + * for all other [WorkflowRendering][com.squareup.workflow1.ui.compose.WorkflowRendering] calls. */ @Immutable private class PreviewViewRegistry( diff --git a/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/ViewFactories.kt b/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/ViewFactories.kt index 783a97df..a27c9647 100644 --- a/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/ViewFactories.kt +++ b/compose-tooling/src/main/java/com/squareup/workflow/ui/compose/tooling/ViewFactories.kt @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) @file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry") package com.squareup.workflow.ui.compose.tooling import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewFactory -import com.squareup.workflow.ui.ViewRegistry import com.squareup.workflow.ui.compose.WorkflowRendering +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** * Draws this [ViewFactory] using a special preview [ViewRegistry]. diff --git a/core-compose/api/core-compose.api b/core-compose/api/core-compose.api index 3ab1ac14..f9bba078 100644 --- a/core-compose/api/core-compose.api +++ b/core-compose/api/core-compose.api @@ -5,48 +5,48 @@ public final class com/squareup/workflow/ui/compose/ComposeRendering { } public final class com/squareup/workflow/ui/compose/ComposeRendering$Companion { - public final fun getFactory ()Lcom/squareup/workflow/ui/ViewFactory; + public final fun getFactory ()Lcom/squareup/workflow1/ui/ViewFactory; public final fun getNoopRendering ()Lcom/squareup/workflow/ui/compose/ComposeRendering; } -public final class com/squareup/workflow/ui/compose/ComposeViewFactory : com/squareup/workflow/ui/ViewFactory { +public final class com/squareup/workflow/ui/compose/ComposeViewFactory : com/squareup/workflow1/ui/ViewFactory { public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function4;)V - public fun buildView (Ljava/lang/Object;Lcom/squareup/workflow/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; + 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; } -public abstract class com/squareup/workflow/ui/compose/ComposeWorkflow : com/squareup/workflow/Workflow { +public abstract class com/squareup/workflow/ui/compose/ComposeWorkflow : com/squareup/workflow1/Workflow { public static final field $stable I public fun ()V - public fun asStatefulWorkflow ()Lcom/squareup/workflow/StatefulWorkflow; - public abstract fun render (Ljava/lang/Object;Lcom/squareup/workflow/Sink;Lcom/squareup/workflow/ui/ViewEnvironment;Landroidx/compose/runtime/Composer;I)V + public fun asStatefulWorkflow ()Lcom/squareup/workflow1/StatefulWorkflow; + public abstract fun render (Ljava/lang/Object;Lcom/squareup/workflow1/Sink;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroidx/compose/runtime/Composer;I)V } public final class com/squareup/workflow/ui/compose/ComposeWorkflowKt { - public static final fun composed (Lcom/squareup/workflow/Workflow$Companion;Lkotlin/jvm/functions/Function5;)Lcom/squareup/workflow/ui/compose/ComposeWorkflow; + public static final fun composed (Lcom/squareup/workflow1/Workflow$Companion;Lkotlin/jvm/functions/Function5;)Lcom/squareup/workflow/ui/compose/ComposeWorkflow; } public final class com/squareup/workflow/ui/compose/CompositionRootKt { - public static final fun withCompositionRoot (Lcom/squareup/workflow/ui/ViewEnvironment;Lkotlin/jvm/functions/Function3;)Lcom/squareup/workflow/ui/ViewEnvironment; - public static final fun withCompositionRoot (Lcom/squareup/workflow/ui/ViewRegistry;Lkotlin/jvm/functions/Function3;)Lcom/squareup/workflow/ui/ViewRegistry; + public static final fun withCompositionRoot (Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function3;)Lcom/squareup/workflow1/ui/ViewEnvironment; + public static final fun withCompositionRoot (Lcom/squareup/workflow1/ui/ViewRegistry;Lkotlin/jvm/functions/Function3;)Lcom/squareup/workflow1/ui/ViewRegistry; } public final class com/squareup/workflow/ui/compose/RenderAsStateKt { - public static final fun renderAsState (Lcom/squareup/workflow/Workflow;Lcom/squareup/workflow/diagnostic/WorkflowDiagnosticListener;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; - public static final fun renderAsState (Lcom/squareup/workflow/Workflow;Ljava/lang/Object;Lcom/squareup/workflow/diagnostic/WorkflowDiagnosticListener;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; - public static final fun renderAsState (Lcom/squareup/workflow/Workflow;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow/diagnostic/WorkflowDiagnosticListener;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; - public static final fun renderAsState (Lcom/squareup/workflow/Workflow;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow/diagnostic/WorkflowDiagnosticListener;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; + public static final fun renderAsState (Lcom/squareup/workflow1/Workflow;Ljava/lang/Object;Ljava/util/List;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; + public static final fun renderAsState (Lcom/squareup/workflow1/Workflow;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Ljava/util/List;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; + public static final fun renderAsState (Lcom/squareup/workflow1/Workflow;Ljava/util/List;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; + public static final fun renderAsState (Lcom/squareup/workflow1/Workflow;Lkotlin/jvm/functions/Function1;Ljava/util/List;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; } public final class com/squareup/workflow/ui/compose/ViewEnvironmentsKt { - public static final fun WorkflowRendering (Ljava/lang/Object;Lcom/squareup/workflow/ui/ViewEnvironment;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V + public static final fun WorkflowRendering (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V } public final class com/squareup/workflow/ui/compose/WorkflowContainerKt { - public static final fun WorkflowContainer (Lcom/squareup/workflow/Workflow;Lcom/squareup/workflow/ui/ViewEnvironment;Landroidx/compose/ui/Modifier;Lcom/squareup/workflow/diagnostic/WorkflowDiagnosticListener;Landroidx/compose/runtime/Composer;II)V - public static final fun WorkflowContainer (Lcom/squareup/workflow/Workflow;Ljava/lang/Object;Lcom/squareup/workflow/ui/ViewEnvironment;Landroidx/compose/ui/Modifier;Lcom/squareup/workflow/diagnostic/WorkflowDiagnosticListener;Landroidx/compose/runtime/Composer;II)V - public static final fun WorkflowContainer (Lcom/squareup/workflow/Workflow;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow/ui/ViewEnvironment;Landroidx/compose/ui/Modifier;Lcom/squareup/workflow/diagnostic/WorkflowDiagnosticListener;Landroidx/compose/runtime/Composer;II)V - public static final fun WorkflowContainer (Lcom/squareup/workflow/Workflow;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow/ui/ViewEnvironment;Landroidx/compose/ui/Modifier;Lcom/squareup/workflow/diagnostic/WorkflowDiagnosticListener;Landroidx/compose/runtime/Composer;II)V + public static final fun WorkflowContainer (Lcom/squareup/workflow1/Workflow;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroidx/compose/ui/Modifier;Ljava/util/List;Landroidx/compose/runtime/Composer;II)V + public static final fun WorkflowContainer (Lcom/squareup/workflow1/Workflow;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroidx/compose/ui/Modifier;Ljava/util/List;Landroidx/compose/runtime/Composer;II)V + public static final fun WorkflowContainer (Lcom/squareup/workflow1/Workflow;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroidx/compose/ui/Modifier;Ljava/util/List;Landroidx/compose/runtime/Composer;II)V + public static final fun WorkflowContainer (Lcom/squareup/workflow1/Workflow;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroidx/compose/ui/Modifier;Ljava/util/List;Landroidx/compose/runtime/Composer;II)V } public final class com/squareup/workflow/ui/core/compose/BuildConfig { diff --git a/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/ComposeViewFactoryTest.kt b/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/ComposeViewFactoryTest.kt index 90450d61..c7a42a82 100644 --- a/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/ComposeViewFactoryTest.kt +++ b/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/ComposeViewFactoryTest.kt @@ -24,9 +24,9 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.viewinterop.AndroidView import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.ui.test.createComposeRule -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewRegistry -import com.squareup.workflow.ui.WorkflowViewStub +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowViewStub import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith diff --git a/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/RenderAsStateTest.kt b/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/RenderAsStateTest.kt index 1d2cf4c7..2f9990b2 100644 --- a/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/RenderAsStateTest.kt +++ b/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/RenderAsStateTest.kt @@ -24,16 +24,16 @@ import androidx.compose.runtime.savedinstancestate.UiSavedStateRegistryAmbient import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.ui.test.createComposeRule import com.google.common.truth.Truth.assertThat -import com.squareup.workflow.RenderContext -import com.squareup.workflow.Snapshot -import com.squareup.workflow.StatefulWorkflow -import com.squareup.workflow.Workflow -import com.squareup.workflow.action -import com.squareup.workflow.parse -import com.squareup.workflow.readUtf8WithLength -import com.squareup.workflow.stateless -import com.squareup.workflow.ui.compose.RenderAsStateTest.SnapshottingWorkflow.SnapshottedRendering -import com.squareup.workflow.writeUtf8WithLength +import com.squareup.workflow1.RenderContext +import com.squareup.workflow1.Snapshot +import com.squareup.workflow1.StatefulWorkflow +import com.squareup.workflow1.Workflow +import com.squareup.workflow1.action +import com.squareup.workflow1.parse +import com.squareup.workflow1.readUtf8WithLength +import com.squareup.workflow1.stateless +import com.squareup.workflow1.ui.compose.RenderAsStateTest.SnapshottingWorkflow.SnapshottedRendering +import com.squareup.workflow1.writeUtf8WithLength import okio.ByteString import okio.ByteString.Companion.decodeBase64 import org.junit.Rule @@ -219,7 +219,7 @@ class RenderAsStateTest { Snapshot.write { it.writeUtf8WithLength(state) } private fun updateString(newString: String) = action { - nextState = newString + state = newString } } } diff --git a/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/ViewEnvironmentsTest.kt b/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/ViewEnvironmentsTest.kt index 23285053..6ac56067 100644 --- a/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/ViewEnvironmentsTest.kt +++ b/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/ViewEnvironmentsTest.kt @@ -21,8 +21,8 @@ import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.onNodeWithText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.ui.test.createComposeRule -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewRegistry +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewRegistry import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith diff --git a/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/WorkflowContainerTest.kt b/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/WorkflowContainerTest.kt index 85f1dedc..9ae9d2b2 100644 --- a/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/WorkflowContainerTest.kt +++ b/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/WorkflowContainerTest.kt @@ -22,10 +22,10 @@ import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.onNodeWithText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.ui.test.createComposeRule -import com.squareup.workflow.Workflow -import com.squareup.workflow.stateless -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewRegistry +import com.squareup.workflow1.Workflow +import com.squareup.workflow1.stateless +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewRegistry import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith diff --git a/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/internal/ViewFactoriesTest.kt b/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/internal/ViewFactoriesTest.kt index 92328e7f..55901e95 100644 --- a/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/internal/ViewFactoriesTest.kt +++ b/core-compose/src/androidTest/java/com/squareup/workflow/ui/compose/internal/ViewFactoriesTest.kt @@ -30,13 +30,13 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.ui.test.createComposeRule -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewFactory -import com.squareup.workflow.ui.ViewRegistry -import com.squareup.workflow.ui.bindShowRendering -import com.squareup.workflow.ui.compose.WorkflowRendering -import com.squareup.workflow.ui.compose.composedViewFactory -import com.squareup.workflow.ui.compose.withCompositionRoot +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.bindShowRendering +import com.squareup.workflow1.ui.compose.WorkflowRendering +import com.squareup.workflow1.ui.compose.composedViewFactory +import com.squareup.workflow1.ui.compose.withCompositionRoot import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeRendering.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeRendering.kt index a90681cc..05877266 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeRendering.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeRendering.kt @@ -20,8 +20,9 @@ package com.squareup.workflow.ui.compose import androidx.compose.runtime.Composable import com.squareup.workflow.ui.compose.ComposeRendering.Companion.Factory import com.squareup.workflow.ui.compose.ComposeRendering.Companion.NoopRendering -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewFactory +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** * A workflow rendering that renders itself using a [Composable] function. @@ -31,6 +32,7 @@ import com.squareup.workflow.ui.ViewFactory * * To use this type, make sure your `ViewRegistry` registers [Factory]. */ +@OptIn(WorkflowUiExperimentalApi::class) class ComposeRendering internal constructor( internal val render: @Composable (ViewEnvironment) -> Unit ) { diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeViewFactory.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeViewFactory.kt index 9f49a802..47d78018 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeViewFactory.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeViewFactory.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) // See https://youtrack.jetbrains.com/issue/KT-31734 @file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry") @@ -26,10 +27,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.ExperimentalComposeApi import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.platform.setContent -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewFactory -import com.squareup.workflow.ui.bindShowRendering +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.bindShowRendering import com.squareup.workflow.ui.compose.internal.ParentComposition +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import kotlin.reflect.KClass /** @@ -59,8 +61,8 @@ import kotlin.reflect.KClass * Workflows can render other workflows, and renderings from one workflow can contain renderings * from other workflows. These renderings may all be bound to their own [ViewFactory]s. Regular * [ViewFactory]s and `LayoutRunner`s use - * [WorkflowViewStub][com.squareup.workflow.ui.WorkflowViewStub] to recursively show nested - * renderings using the [ViewRegistry][com.squareup.workflow.ui.ViewRegistry]. + * [WorkflowViewStub][com.squareup.workflow1.ui.WorkflowViewStub] to recursively show nested + * renderings using the [ViewRegistry][com.squareup.workflow1.ui.ViewRegistry]. * * View factories defined using this function may also show nested renderings. Doing so is as simple * as calling [WorkflowRendering] and passing in the nested rendering. See the kdoc on that function diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeWorkflow.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeWorkflow.kt index 726bc5e7..4d379b9b 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeWorkflow.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/ComposeWorkflow.kt @@ -13,23 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) @file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry") package com.squareup.workflow.ui.compose import androidx.compose.runtime.Composable -import com.squareup.workflow.Sink -import com.squareup.workflow.StatefulWorkflow -import com.squareup.workflow.Workflow -import com.squareup.workflow.ui.ViewEnvironment +import com.squareup.workflow1.Sink +import com.squareup.workflow1.StatefulWorkflow +import com.squareup.workflow1.Workflow +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow.ui.compose.internal.ComposeWorkflowImpl /** - * A stateless [Workflow][com.squareup.workflow.Workflow] that [renders][render] itself as + * A stateless [Workflow][com.squareup.workflow1.Workflow] that [renders][render] itself as * [Composable] function. Effectively defines an inline - * [composedViewFactory][com.squareup.workflow.ui.compose.composedViewFactory]. + * [composedViewFactory][com.squareup.workflow1.ui.compose.composedViewFactory]. * - * This workflow does not have access to a [RenderContext][com.squareup.workflow.RenderContext] + * This workflow does not have access to a [RenderContext][com.squareup.workflow1.RenderContext] * since render contexts are only valid during render passes, and this workflow's [render] method * is invoked after the render pass, when view bindings are being shown. * diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/CompositionRoot.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/CompositionRoot.kt index c1325e45..e9e4ccab 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/CompositionRoot.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/CompositionRoot.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) @file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry") package com.squareup.workflow.ui.compose @@ -23,8 +24,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Providers import androidx.compose.runtime.remember import androidx.compose.runtime.staticAmbientOf -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewRegistry +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow.ui.compose.internal.mapFactories /** diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/RenderAsState.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/RenderAsState.kt index b275b9fb..85132435 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/RenderAsState.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/RenderAsState.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(ExperimentalWorkflowApi::class) @file:Suppress("NOTHING_TO_INLINE") package com.squareup.workflow.ui.compose @@ -20,28 +21,25 @@ package com.squareup.workflow.ui.compose import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLifecycleObserver -import androidx.compose.runtime.MutableState import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.savedinstancestate.Saver import androidx.compose.runtime.savedinstancestate.SaverScope -import androidx.compose.runtime.savedinstancestate.savedInstanceState -import androidx.compose.ui.node.Ref -import com.squareup.workflow.Snapshot -import com.squareup.workflow.Workflow -import com.squareup.workflow.diagnostic.WorkflowDiagnosticListener -import com.squareup.workflow.launchWorkflowIn +import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState +import com.squareup.workflow1.ExperimentalWorkflowApi +import com.squareup.workflow1.Snapshot +import com.squareup.workflow1.TreeSnapshot +import com.squareup.workflow1.Workflow +import com.squareup.workflow1.WorkflowInterceptor +import com.squareup.workflow1.renderWorkflowIn import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.consumeAsFlow -import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.plus import okio.ByteString /** @@ -54,23 +52,25 @@ import okio.ByteString * whenever the runtime emits a new rendering. * * [Snapshot]s from the runtime will automatically be saved to the current - * [UiSavedStateRegistry][androidx.ui.savedinstancestate.UiSavedStateRegistry]. When the runtime is - * started, if a snapshot exists in the registry, it will be used to restore the workflows. + * [UiSavedStateRegistry][androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry]. When + * the runtime is started, if a snapshot exists in the registry, it will be used to restore the + * workflows. * * @receiver The [Workflow] to run. If the value of the receiver changes to a different [Workflow] * while this function is in the composition, the runtime will be restarted with the new workflow. * @param props The [PropsT] for the root [Workflow]. Changes to this value across different * compositions will cause the root workflow to re-render with the new props. * @param onOutput A function that will be executed whenever the root [Workflow] emits an output. - * @param diagnosticListener An optional [WorkflowDiagnosticListener] to start the runtime with. If - * this value changes while this function is in the composition, the runtime will be restarted. + * @param interceptors An optional list of [WorkflowInterceptor]s that will wrap every workflow + * rendered by the runtime. Interceptors will be invoked in 0-to-length order: the interceptor at + * index 0 will process the workflow first, then the interceptor at index 1, etc. */ @Composable fun Workflow.renderAsState( props: PropsT, onOutput: (OutputT) -> Unit, - diagnosticListener: WorkflowDiagnosticListener? = null -): State = renderAsStateImpl(this, props, onOutput, diagnosticListener) + interceptors: List = emptyList() +): State = renderAsStateImpl(this, props, onOutput, interceptors) /** * Runs this [Workflow] as long as this composable is part of the composition, and returns a @@ -82,20 +82,22 @@ fun Workflow.re * whenever the runtime emits a new rendering. * * [Snapshot]s from the runtime will automatically be saved to the current - * [UiSavedStateRegistry][androidx.ui.savedinstancestate.UiSavedStateRegistry]. When the runtime is - * started, if a snapshot exists in the registry, it will be used to restore the workflows. + * [UiSavedStateRegistry][androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry]. When + * the runtime is started, if a snapshot exists in the registry, it will be used to restore the + * workflows. * * @receiver The [Workflow] to run. If the value of the receiver changes to a different [Workflow] * while this function is in the composition, the runtime will be restarted with the new workflow. * @param onOutput A function that will be executed whenever the root [Workflow] emits an output. - * @param diagnosticListener An optional [WorkflowDiagnosticListener] to start the runtime with. If - * this value changes while this function is in the composition, the runtime will be restarted. + * @param interceptors An optional list of [WorkflowInterceptor]s that will wrap every workflow + * rendered by the runtime. Interceptors will be invoked in 0-to-length order: the interceptor at + * index 0 will process the workflow first, then the interceptor at index 1, etc. */ @Composable inline fun Workflow.renderAsState( noinline onOutput: (OutputT) -> Unit, - diagnosticListener: WorkflowDiagnosticListener? = null -): State = renderAsState(Unit, onOutput, diagnosticListener) + interceptors: List = emptyList() +): State = renderAsState(Unit, onOutput, interceptors) /** * Runs this [Workflow] as long as this composable is part of the composition, and returns a @@ -107,21 +109,23 @@ inline fun Workflow.rende * whenever the runtime emits a new rendering. * * [Snapshot]s from the runtime will automatically be saved to the current - * [UiSavedStateRegistry][androidx.ui.savedinstancestate.UiSavedStateRegistry]. When the runtime is - * started, if a snapshot exists in the registry, it will be used to restore the workflows. + * [UiSavedStateRegistry][androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry]. When + * the runtime is started, if a snapshot exists in the registry, it will be used to restore the + * workflows. * * @receiver The [Workflow] to run. If the value of the receiver changes to a different [Workflow] * while this function is in the composition, the runtime will be restarted with the new workflow. * @param props The [PropsT] for the root [Workflow]. Changes to this value across different * compositions will cause the root workflow to re-render with the new props. - * @param diagnosticListener An optional [WorkflowDiagnosticListener] to start the runtime with. If - * this value changes while this function is in the composition, the runtime will be restarted. + * @param interceptors An optional list of [WorkflowInterceptor]s that will wrap every workflow + * rendered by the runtime. Interceptors will be invoked in 0-to-length order: the interceptor at + * index 0 will process the workflow first, then the interceptor at index 1, etc. */ @Composable inline fun Workflow.renderAsState( props: PropsT, - diagnosticListener: WorkflowDiagnosticListener? = null -): State = renderAsState(props, {}, diagnosticListener) + interceptors: List = emptyList() +): State = renderAsState(props, {}, interceptors) /** * Runs this [Workflow] as long as this composable is part of the composition, and returns a @@ -133,18 +137,20 @@ inline fun Workflow.renderAsSt * whenever the runtime emits a new rendering. * * [Snapshot]s from the runtime will automatically be saved to the current - * [UiSavedStateRegistry][androidx.ui.savedinstancestate.UiSavedStateRegistry]. When the runtime is - * started, if a snapshot exists in the registry, it will be used to restore the workflows. + * [UiSavedStateRegistry][androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry]. When + * the runtime is started, if a snapshot exists in the registry, it will be used to restore the + * workflows. * * @receiver The [Workflow] to run. If the value of the receiver changes to a different [Workflow] * while this function is in the composition, the runtime will be restarted with the new workflow. - * @param diagnosticListener An optional [WorkflowDiagnosticListener] to start the runtime with. If - * this value changes while this function is in the composition, the runtime will be restarted. + * @param interceptors An optional list of [WorkflowInterceptor]s that will wrap every workflow + * rendered by the runtime. Interceptors will be invoked in 0-to-length order: the interceptor at + * index 0 will process the workflow first, then the interceptor at index 1, etc. */ @Composable inline fun Workflow.renderAsState( - diagnosticListener: WorkflowDiagnosticListener? = null -): State = renderAsState(Unit, {}, diagnosticListener) + interceptors: List = emptyList() +): State = renderAsState(Unit, {}, interceptors) /** * @param snapshotKey Allows tests to pass in a custom key to use to save/restore the snapshot from @@ -155,22 +161,25 @@ inline fun Workflow.renderAsState( workflow: Workflow, props: PropsT, onOutput: (OutputT) -> Unit, - diagnosticListener: WorkflowDiagnosticListener?, + interceptors: List, snapshotKey: String? = null ): State { + // TODO pretty sure rememberCoroutineScope() uses an async Handler so we don't need to use Main.immediate anymore. // TODO Pass Dispatchers.Main.immediate and merge two scope vals when this bug is fixed: // https://issuetracker.google.com/issues/165674304 - val baseScope = rememberCoroutineScope() - val workflowScope = remember { baseScope + Dispatchers.Main.immediate } - val snapshotState = savedInstanceState(key = snapshotKey, saver = SnapshotSaver) { null } - - val outputRef = remember { Ref<(OutputT) -> Unit>() } - outputRef.value = onOutput + // val baseScope = rememberCoroutineScope() + // val workflowScope = remember { baseScope + Dispatchers.Main.immediate } + val workflowScope = rememberCoroutineScope() + val outputState = rememberUpdatedState(onOutput) + val snapshotHolder = rememberSavedInstanceState(key = snapshotKey, saver = TreeSnapshotHolder) { + TreeSnapshotHolder() + } // We can't use onActive/on(Pre)Commit because they won't run their callback until after this // function returns, and we need to run this immediately so we get the rendering synchronously. - val state = remember(workflow, diagnosticListener) { - WorkflowState(workflowScope, workflow, props, outputRef, snapshotState, diagnosticListener) + val state = remember(workflow, interceptors) { + WorkflowState(workflowScope, workflow, props, outputState, snapshotHolder, interceptors) + .apply { start() } } state.setProps(props) @@ -180,60 +189,64 @@ inline fun Workflow.renderAsState( @Suppress("EXPERIMENTAL_API_USAGE") private class WorkflowState( private val workflowScope: CoroutineScope, - workflow: Workflow, + private val workflow: Workflow, initialProps: PropsT, - private val outputRef: Ref<(OutputT) -> Unit>, - private val snapshotState: MutableState, - private val diagnosticListener: WorkflowDiagnosticListener? + private val outputState: State<(OutputT) -> Unit>, + private val snapshotHolder: TreeSnapshotHolder, + private val interceptors: List ) : CompositionLifecycleObserver { private val renderingState = mutableStateOf(null) - - // This can be a StateFlow once coroutines is upgraded to 1.3.6. - private val propsChannel = Channel(capacity = Channel.CONFLATED) - .apply { offer(initialProps) } - val propsFlow = propsChannel.consumeAsFlow() - .distinctUntilChanged() + private val propsFlow = MutableStateFlow(initialProps) // The value is guaranteed to be set before returning, so this cast is fine. @Suppress("UNCHECKED_CAST") val rendering: State get() = renderingState as State - init { - launchWorkflowIn(workflowScope, workflow, propsFlow, snapshotState.value) { session -> - session.diagnosticListener = diagnosticListener - - session.outputs.onEach { outputRef.value!!.invoke(it) } - .launchIn(this) - - session.renderingsAndSnapshots - .onEach { (rendering, snapshot) -> - renderingState.value = rendering - snapshotState.value = snapshot - } - .launchIn(this) + fun start() { + println("OMG WorkflowState starting") + val renderingsFlow = renderWorkflowIn( + workflow, workflowScope, propsFlow, snapshotHolder.snapshot, interceptors + ) { output -> + outputState.value.invoke(output) } + + renderingsFlow + .onEach { (rendering, snapshot) -> + renderingState.value = rendering + snapshotHolder.snapshot = snapshot + } + .launchIn(workflowScope) } fun setProps(props: PropsT) { - propsChannel.offer(props) + propsFlow.value = props } - override fun onEnter() {} + override fun onEnter() { + // Nothing to do. + println("OMG WorkflowState onEnter") + } override fun onLeave() { workflowScope.cancel() } } -private object SnapshotSaver : Saver { - override fun SaverScope.save(value: Snapshot?): ByteArray { - return value?.bytes?.toByteArray() ?: ByteArray(0) - } +private class TreeSnapshotHolder { + + var snapshot: TreeSnapshot? = null - override fun restore(value: ByteArray): Snapshot? { - return value.takeUnless { it.isEmpty() } - ?.let { bytes -> Snapshot.of(ByteString.of(*bytes)) } + companion object : Saver { + override fun SaverScope.save(value: TreeSnapshotHolder): ByteArray? { + return value.snapshot?.toByteString()?.toByteArray() + } + + override fun restore(value: ByteArray): TreeSnapshotHolder { + return value.takeUnless { it.isEmpty() } + ?.let { bytes -> TreeSnapshot.parse(ByteString.of(*bytes)) } + .let { TreeSnapshotHolder().apply { snapshot = it } } + } } } diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/ViewEnvironments.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/ViewEnvironments.kt index 18476550..079fb9d2 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/ViewEnvironments.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/ViewEnvironments.kt @@ -13,21 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.workflow.ui.compose import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewRegistry import com.squareup.workflow.ui.compose.internal.WorkflowRendering +import com.squareup.workflow1.ui.AndroidViewRendering +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** * Renders [rendering] into the composition using this [ViewEnvironment]'s - * [ViewRegistry][com.squareup.workflow.ui.ViewRegistry] to generate the view. + * [ViewRegistry][com.squareup.workflow1.ui.ViewRegistry] to generate the view. * * This function fulfills a similar role as - * [WorkflowViewStub][com.squareup.workflow.ui.WorkflowViewStub], but is much more convenient to use + * [WorkflowViewStub][com.squareup.workflow1.ui.WorkflowViewStub], but is much more convenient to use * from Composable functions. * * ## Example @@ -46,21 +51,32 @@ import com.squareup.workflow.ui.compose.internal.WorkflowRendering * ``` * * @param rendering The workflow rendering to display. May be of any type for which a - * [ViewFactory][com.squareup.workflow.ui.ViewFactory] has been registered in this + * [ViewFactory][com.squareup.workflow1.ui.ViewFactory] has been registered in this * environment's [ViewRegistry]. * @param modifier A [Modifier] that will be applied to composable used to show [rendering]. * * @throws IllegalArgumentException if no factory can be found for [rendering]'s type. */ -@Composable fun WorkflowRendering( - rendering: Any, +@Composable fun WorkflowRendering( + rendering: RenderingT, viewEnvironment: ViewEnvironment, modifier: Modifier = Modifier ) { val viewRegistry = remember(viewEnvironment) { viewEnvironment[ViewRegistry] } val renderingType = rendering::class val viewFactory = remember(viewRegistry, renderingType) { + @Suppress("UNCHECKED_CAST") viewRegistry.getFactoryFor(renderingType) + // These null case handlers are simply copied from the implementation of + // ViewRegistry.buildView. Once this code is merged into the main workflow repository, the + // shared code should be extracted. It's not worth exposing this as public API in the main + // workflow library just for this. + ?: ((rendering as? AndroidViewRendering<*>)?.viewFactory as ViewFactory) + ?: throw IllegalArgumentException( + "A ${ViewFactory::class.qualifiedName} should have been registered " + + "to display ${renderingType.qualifiedName} instances, or that class " + + "should implement ${ViewFactory::class.simpleName}<${renderingType.simpleName}>." + ) } WorkflowRendering(rendering, viewFactory, viewEnvironment, modifier) } diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/WorkflowContainer.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/WorkflowContainer.kt index 9b82e671..2ba30035 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/WorkflowContainer.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/WorkflowContainer.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(ExperimentalWorkflowApi::class, WorkflowUiExperimentalApi::class) @file:Suppress( "FunctionNaming", "NOTHING_TO_INLINE" @@ -23,24 +24,27 @@ package com.squareup.workflow.ui.compose import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import com.squareup.workflow.Snapshot -import com.squareup.workflow.Workflow -import com.squareup.workflow.diagnostic.WorkflowDiagnosticListener -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewFactory -import com.squareup.workflow.ui.ViewRegistry -import com.squareup.workflow.ui.plus +import com.squareup.workflow1.ExperimentalWorkflowApi +import com.squareup.workflow1.Snapshot +import com.squareup.workflow1.Workflow +import com.squareup.workflow1.WorkflowInterceptor +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi +import com.squareup.workflow1.ui.plus /** * Render a [Workflow]'s renderings. * * When this function is first composed it will start a new runtime. This runtime will be restarted - * any time [workflow], [diagnosticListener], or the `CoroutineContext` + * any time [workflow], [interceptors], or the `CoroutineContext` * changes. The runtime will be cancelled when this function stops composing. * * [Snapshot]s from the runtime will automatically be saved to the current - * [UiSavedStateRegistry][androidx.ui.savedinstancestate.UiSavedStateRegistry]. When the runtime is - * started, if a snapshot exists in the registry, it will be used to restore the workflows. + * [UiSavedStateRegistry][androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry]. When + * the runtime is started, if a snapshot exists in the registry, it will be used to restore the + * workflows. * * @param workflow The [Workflow] to render. * @param props The props to render the root workflow with. If this value changes between calls, @@ -48,7 +52,9 @@ import com.squareup.workflow.ui.plus * @param onOutput A function that will be invoked any time the root workflow emits an output. * @param viewEnvironment The [ViewEnvironment] used to display renderings. * @param modifier The [Modifier] to apply to the root [ViewFactory]. - * @param diagnosticListener A [WorkflowDiagnosticListener] to configure on the runtime. + * @param interceptors An optional list of [WorkflowInterceptor]s that will wrap every workflow + * rendered by the runtime. Interceptors will be invoked in 0-to-length order: the interceptor at + * index 0 will process the workflow first, then the interceptor at index 1, etc. */ @Composable fun WorkflowContainer( workflow: Workflow, @@ -56,14 +62,14 @@ import com.squareup.workflow.ui.plus onOutput: (OutputT) -> Unit, viewEnvironment: ViewEnvironment, modifier: Modifier = Modifier, - diagnosticListener: WorkflowDiagnosticListener? = null + interceptors: List = emptyList() ) { // Ensure ComposeRendering is in the ViewRegistry. val realEnvironment = remember(viewEnvironment) { viewEnvironment.withFactory(ComposeRendering.Factory) } - val rendering = workflow.renderAsState(props, onOutput, diagnosticListener) + val rendering = workflow.renderAsState(props, onOutput, interceptors) WorkflowRendering(rendering.value, realEnvironment, modifier) } @@ -71,80 +77,89 @@ import com.squareup.workflow.ui.plus * Render a [Workflow]'s renderings. * * When this function is first composed it will start a new runtime. This runtime will be restarted - * any time [workflow], [diagnosticListener], or the `CoroutineContext` + * any time [workflow], [interceptors], or the `CoroutineContext` * changes. The runtime will be cancelled when this function stops composing. * * [Snapshot]s from the runtime will automatically be saved to the current - * [UiSavedStateRegistry][androidx.ui.savedinstancestate.UiSavedStateRegistry]. When the runtime is - * started, if a snapshot exists in the registry, it will be used to restore the workflows. + * [UiSavedStateRegistry][androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry]. When + * the runtime is started, if a snapshot exists in the registry, it will be used to restore the + * workflows. * * @param workflow The [Workflow] to render. * @param onOutput A function that will be invoked any time the root workflow emits an output. * @param viewEnvironment The [ViewEnvironment] used to display renderings. * @param modifier The [Modifier] to apply to the root [ViewFactory]. - * @param diagnosticListener A [WorkflowDiagnosticListener] to configure on the runtime. + * @param interceptors An optional list of [WorkflowInterceptor]s that will wrap every workflow + * rendered by the runtime. Interceptors will be invoked in 0-to-length order: the interceptor at + * index 0 will process the workflow first, then the interceptor at index 1, etc. */ @Composable inline fun WorkflowContainer( workflow: Workflow, noinline onOutput: (OutputT) -> Unit, viewEnvironment: ViewEnvironment, modifier: Modifier = Modifier, - diagnosticListener: WorkflowDiagnosticListener? = null + interceptors: List = emptyList() ) { - WorkflowContainer(workflow, Unit, onOutput, viewEnvironment, modifier, diagnosticListener) + WorkflowContainer(workflow, Unit, onOutput, viewEnvironment, modifier, interceptors) } /** * Render a [Workflow]'s renderings. * * When this function is first composed it will start a new runtime. This runtime will be restarted - * any time [workflow], [diagnosticListener], or the `CoroutineContext` + * any time [workflow], [interceptors], or the `CoroutineContext` * changes. The runtime will be cancelled when this function stops composing. * * [Snapshot]s from the runtime will automatically be saved to the current - * [UiSavedStateRegistry][androidx.ui.savedinstancestate.UiSavedStateRegistry]. When the runtime is - * started, if a snapshot exists in the registry, it will be used to restore the workflows. + * [UiSavedStateRegistry][androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry]. When + * the runtime is started, if a snapshot exists in the registry, it will be used to restore the + * workflows. * * @param workflow The [Workflow] to render. * @param props The props to render the root workflow with. If this value changes between calls, * the workflow runtime will re-render with the new props. * @param viewEnvironment The [ViewEnvironment] used to display renderings. * @param modifier The [Modifier] to apply to the root [ViewFactory]. - * @param diagnosticListener A [WorkflowDiagnosticListener] to configure on the runtime. + * @param interceptors An optional list of [WorkflowInterceptor]s that will wrap every workflow + * rendered by the runtime. Interceptors will be invoked in 0-to-length order: the interceptor at + * index 0 will process the workflow first, then the interceptor at index 1, etc. */ @Composable inline fun WorkflowContainer( workflow: Workflow, props: PropsT, viewEnvironment: ViewEnvironment, modifier: Modifier = Modifier, - diagnosticListener: WorkflowDiagnosticListener? = null + interceptors: List = emptyList() ) { - WorkflowContainer(workflow, props, {}, viewEnvironment, modifier, diagnosticListener) + WorkflowContainer(workflow, props, {}, viewEnvironment, modifier, interceptors) } /** * Render a [Workflow]'s renderings. * * When this function is first composed it will start a new runtime. This runtime will be restarted - * any time [workflow], [diagnosticListener], or the `CoroutineContext` + * any time [workflow], [interceptors], or the `CoroutineContext` * changes. The runtime will be cancelled when this function stops composing. * * [Snapshot]s from the runtime will automatically be saved to the current - * [UiSavedStateRegistry][androidx.ui.savedinstancestate.UiSavedStateRegistry]. When the runtime is - * started, if a snapshot exists in the registry, it will be used to restore the workflows. + * [UiSavedStateRegistry][androidx.compose.runtime.savedinstancestate.UiSavedStateRegistry]. When + * the runtime is started, if a snapshot exists in the registry, it will be used to restore the + * workflows. * * @param workflow The [Workflow] to render. * @param viewEnvironment The [ViewEnvironment] used to display renderings. * @param modifier The [Modifier] to apply to the root [ViewFactory]. - * @param diagnosticListener A [WorkflowDiagnosticListener] to configure on the runtime. + * @param interceptors An optional list of [WorkflowInterceptor]s that will wrap every workflow + * rendered by the runtime. Interceptors will be invoked in 0-to-length order: the interceptor at + * index 0 will process the workflow first, then the interceptor at index 1, etc. */ @Composable inline fun WorkflowContainer( workflow: Workflow, viewEnvironment: ViewEnvironment, modifier: Modifier = Modifier, - diagnosticListener: WorkflowDiagnosticListener? = null + interceptors: List = emptyList() ) { - WorkflowContainer(workflow, Unit, {}, viewEnvironment, modifier, diagnosticListener) + WorkflowContainer(workflow, Unit, {}, viewEnvironment, modifier, interceptors) } private fun ViewEnvironment.withFactory(viewFactory: ViewFactory<*>): ViewEnvironment { diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ComposeWorkflowImpl.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ComposeWorkflowImpl.kt index 7e59a076..8fff2d0c 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ComposeWorkflowImpl.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ComposeWorkflowImpl.kt @@ -18,15 +18,14 @@ package com.squareup.workflow.ui.compose.internal import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.structuralEqualityPolicy -import com.squareup.workflow.RenderContext -import com.squareup.workflow.Sink -import com.squareup.workflow.Snapshot -import com.squareup.workflow.StatefulWorkflow -import com.squareup.workflow.action -import com.squareup.workflow.contraMap import com.squareup.workflow.ui.compose.ComposeRendering import com.squareup.workflow.ui.compose.ComposeWorkflow import com.squareup.workflow.ui.compose.internal.ComposeWorkflowImpl.State +import com.squareup.workflow1.Sink +import com.squareup.workflow1.Snapshot +import com.squareup.workflow1.StatefulWorkflow +import com.squareup.workflow1.action +import com.squareup.workflow1.contraMap internal class ComposeWorkflowImpl( private val workflow: ComposeWorkflow @@ -67,7 +66,7 @@ internal class ComposeWorkflowImpl( override fun render( props: PropsT, state: State, - context: RenderContext, OutputT> + context: RenderContext ): ComposeRendering { // The first render pass needs to cache the sink. The sink is reusable, so we can just pass the // same one every time. diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ParentComposition.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ParentComposition.kt index 247ca15f..25672de0 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ParentComposition.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ParentComposition.kt @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) @file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry") package com.squareup.workflow.ui.compose.internal import androidx.compose.runtime.CompositionReference -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewEnvironmentKey +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewEnvironmentKey +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** * Holds a [CompositionReference] that can be passed to [setContent] to create a composition that is diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ViewFactories.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ViewFactories.kt index 1576e4ff..fcaa660b 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ViewFactories.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ViewFactories.kt @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.workflow.ui.compose.internal import android.content.Context @@ -27,12 +29,13 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.node.Ref import androidx.compose.ui.viewinterop.AndroidView -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewFactory -import com.squareup.workflow.ui.canShowRendering import com.squareup.workflow.ui.compose.ComposeViewFactory -import com.squareup.workflow.ui.getRendering -import com.squareup.workflow.ui.showRendering +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi +import com.squareup.workflow1.ui.canShowRendering +import com.squareup.workflow1.ui.getRendering +import com.squareup.workflow1.ui.showRendering import kotlin.properties.Delegates.observable /** @@ -68,7 +71,7 @@ import kotlin.properties.Delegates.observable } /** - * This is effectively the logic of [com.squareup.workflow.ui.WorkflowViewStub], but translated + * This is effectively the logic of [com.squareup.workflow1.ui.WorkflowViewStub], but translated * into Compose idioms. This approach has a few advantages: * * - Avoids extra custom views required to host `WorkflowViewStub` inside a Composition. Its trick @@ -120,7 +123,7 @@ import kotlin.properties.Delegates.observable * of looking one up itself, and doesn't do the replace-in-parent trick. * * It doesn't seem possible to create the view inside a Composable directly and use - * [androidx.ui.viewinterop.AndroidView]. I can't figure out exactly why it doesn't work, but I + * [AndroidView]. I can't figure out exactly why it doesn't work, but I * think it has something to do with getting into an incorrect state if a non-Composable view * factory synchronously builds and binds a ComposableViewFactory in buildView. In that case, the * second and subsequent compose passes will lose ambients from the parent composition. I've spent diff --git a/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ViewRegistries.kt b/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ViewRegistries.kt index 8bec77e0..474f9ae1 100644 --- a/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ViewRegistries.kt +++ b/core-compose/src/main/java/com/squareup/workflow/ui/compose/internal/ViewRegistries.kt @@ -13,10 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.workflow.ui.compose.internal -import com.squareup.workflow.ui.ViewFactory -import com.squareup.workflow.ui.ViewRegistry +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import kotlin.reflect.KClass /** @@ -30,8 +33,10 @@ internal fun ViewRegistry.mapFactories( override fun getFactoryFor( renderingType: KClass - ): ViewFactory { - val transformedFactory = transform(this@mapFactories.getFactoryFor(renderingType)) + ): ViewFactory? { + val transformedFactory = this@mapFactories.getFactoryFor(renderingType) + ?.let(transform) + ?: return null check(transformedFactory.type == renderingType) { "Expected transform to return a ViewFactory that is compatible with $renderingType, " + "but got one with type ${transformedFactory.type}" diff --git a/samples/src/main/java/com/squareup/sample/hellocompose/App.kt b/samples/src/main/java/com/squareup/sample/hellocompose/App.kt index eeeeb076..5a8650ee 100644 --- a/samples/src/main/java/com/squareup/sample/hellocompose/App.kt +++ b/samples/src/main/java/com/squareup/sample/hellocompose/App.kt @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.sample.hellocompose import androidx.compose.foundation.border @@ -23,13 +25,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.ui.tooling.preview.Preview -import com.squareup.workflow.diagnostic.SimpleLoggingDiagnosticListener -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewRegistry import com.squareup.workflow.ui.compose.WorkflowContainer +import com.squareup.workflow1.SimpleLoggingWorkflowInterceptor +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi private val viewRegistry = ViewRegistry(HelloBinding) -private val viewEnvironment = ViewEnvironment(viewRegistry) +private val viewEnvironment = ViewEnvironment(mapOf(ViewRegistry to viewRegistry)) @Composable fun App() { MaterialTheme { @@ -40,7 +43,7 @@ private val viewEnvironment = ViewEnvironment(viewRegistry) width = 10.dp, color = Color.Magenta ), - diagnosticListener = SimpleLoggingDiagnosticListener() + interceptors = listOf(SimpleLoggingWorkflowInterceptor()) ) } } diff --git a/samples/src/main/java/com/squareup/sample/hellocompose/HelloWorkflow.kt b/samples/src/main/java/com/squareup/sample/hellocompose/HelloWorkflow.kt index 84f4bd65..ee052d9c 100644 --- a/samples/src/main/java/com/squareup/sample/hellocompose/HelloWorkflow.kt +++ b/samples/src/main/java/com/squareup/sample/hellocompose/HelloWorkflow.kt @@ -19,11 +19,11 @@ import com.squareup.sample.hellocompose.HelloWorkflow.Rendering import com.squareup.sample.hellocompose.HelloWorkflow.State import com.squareup.sample.hellocompose.HelloWorkflow.State.Goodbye import com.squareup.sample.hellocompose.HelloWorkflow.State.Hello -import com.squareup.workflow.RenderContext -import com.squareup.workflow.Snapshot -import com.squareup.workflow.StatefulWorkflow -import com.squareup.workflow.action -import com.squareup.workflow.parse +import com.squareup.workflow1.BaseRenderContext +import com.squareup.workflow1.Snapshot +import com.squareup.workflow1.StatefulWorkflow +import com.squareup.workflow1.action +import com.squareup.workflow1.parse object HelloWorkflow : StatefulWorkflow() { enum class State { @@ -42,7 +42,7 @@ object HelloWorkflow : StatefulWorkflow() { ) private val helloAction = action { - nextState = nextState.theOtherState() + state = state.theOtherState() } override fun initialState( @@ -54,11 +54,22 @@ object HelloWorkflow : StatefulWorkflow() { override fun render( props: Unit, state: State, - context: RenderContext - ): Rendering = Rendering( - message = state.name, - onClick = { context.actionSink.send(helloAction) } - ) + context: RenderContext + ): Rendering { + // TODO This currently causes an IR generation error. I think this is fixed in 1.4.20, but can't + // build with that until compose supports it. If fixed, inline doRender. + return doRender(state, context) + } + + private fun doRender( + state: State, + context: BaseRenderContext + ): Rendering { + return Rendering( + message = state.name, + onClick = { context.actionSink.send(helloAction) } + ) + } override fun snapshotState(state: State): Snapshot = Snapshot.of(if (state == Hello) 1 else 0) } diff --git a/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloBinding.kt b/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloBinding.kt index cf0b5f11..d20f67df 100644 --- a/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloBinding.kt +++ b/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloBinding.kt @@ -15,10 +15,10 @@ */ package com.squareup.sample.hellocomposebinding -import androidx.compose.material.Text import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier diff --git a/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloBindingActivity.kt b/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloBindingActivity.kt index 0a761325..c1a4f3f1 100644 --- a/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloBindingActivity.kt +++ b/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloBindingActivity.kt @@ -13,22 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.sample.hellocomposebinding import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.compose.material.MaterialTheme -import com.squareup.workflow.diagnostic.SimpleLoggingDiagnosticListener -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewRegistry -import com.squareup.workflow.ui.WorkflowRunner import com.squareup.workflow.ui.compose.withCompositionRoot -import com.squareup.workflow.ui.setContentWorkflow +import com.squareup.workflow1.SimpleLoggingWorkflowInterceptor +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowRunner +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi +import com.squareup.workflow1.ui.setContentWorkflow private val viewRegistry = ViewRegistry(HelloBinding) -private val containerHints = ViewEnvironment(viewRegistry).withCompositionRoot { content -> - MaterialTheme(content = content) -} +private val containerHints = ViewEnvironment(mapOf(ViewRegistry to viewRegistry)) + .withCompositionRoot { content -> + MaterialTheme(content = content) + } class HelloBindingActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -36,7 +40,7 @@ class HelloBindingActivity : AppCompatActivity() { setContentWorkflow(containerHints) { WorkflowRunner.Config( HelloWorkflow, - diagnosticListener = SimpleLoggingDiagnosticListener() + interceptors = listOf(SimpleLoggingWorkflowInterceptor()) ) } } diff --git a/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloWorkflow.kt b/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloWorkflow.kt index 42a7e8bc..de78b289 100644 --- a/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloWorkflow.kt +++ b/samples/src/main/java/com/squareup/sample/hellocomposebinding/HelloWorkflow.kt @@ -19,11 +19,11 @@ import com.squareup.sample.hellocomposebinding.HelloWorkflow.Rendering import com.squareup.sample.hellocomposebinding.HelloWorkflow.State import com.squareup.sample.hellocomposebinding.HelloWorkflow.State.Goodbye import com.squareup.sample.hellocomposebinding.HelloWorkflow.State.Hello -import com.squareup.workflow.RenderContext -import com.squareup.workflow.Snapshot -import com.squareup.workflow.StatefulWorkflow -import com.squareup.workflow.action -import com.squareup.workflow.parse +import com.squareup.workflow1.RenderContext +import com.squareup.workflow1.Snapshot +import com.squareup.workflow1.StatefulWorkflow +import com.squareup.workflow1.action +import com.squareup.workflow1.parse object HelloWorkflow : StatefulWorkflow() { enum class State { @@ -42,7 +42,7 @@ object HelloWorkflow : StatefulWorkflow() { ) private val helloAction = action { - nextState = nextState.theOtherState() + state = state.theOtherState() } override fun initialState( @@ -54,7 +54,7 @@ object HelloWorkflow : StatefulWorkflow() { override fun render( props: Unit, state: State, - context: RenderContext + context: RenderContext ): Rendering { return Rendering( message = state.name, diff --git a/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingActivity.kt b/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingActivity.kt index 2877316d..f1d2679a 100644 --- a/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingActivity.kt +++ b/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloComposeRenderingActivity.kt @@ -13,15 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.sample.hellocomposerendering import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.squareup.workflow.ui.compose.ComposeRendering -import com.squareup.workflow.diagnostic.SimpleLoggingDiagnosticListener -import com.squareup.workflow.ui.ViewRegistry -import com.squareup.workflow.ui.WorkflowRunner -import com.squareup.workflow.ui.setContentWorkflow +import com.squareup.workflow1.SimpleLoggingWorkflowInterceptor +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowRunner +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi +import com.squareup.workflow1.ui.setContentWorkflow private val viewRegistry = ViewRegistry(ComposeRendering.Factory) @@ -31,7 +34,7 @@ class HelloComposeRenderingActivity : AppCompatActivity() { setContentWorkflow(viewRegistry) { WorkflowRunner.Config( HelloWorkflow, - diagnosticListener = SimpleLoggingDiagnosticListener() + interceptors = listOf(SimpleLoggingWorkflowInterceptor()) ) } } diff --git a/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloRenderingWorkflow.kt b/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloRenderingWorkflow.kt index 0e8d2326..3a7c559e 100644 --- a/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloRenderingWorkflow.kt +++ b/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloRenderingWorkflow.kt @@ -13,22 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.sample.hellocomposerendering -import androidx.compose.material.Text import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.ui.tooling.preview.Preview import com.squareup.sample.hellocomposerendering.HelloRenderingWorkflow.Toggle -import com.squareup.workflow.Sink -import com.squareup.workflow.ui.ViewEnvironment import com.squareup.workflow.ui.compose.ComposeWorkflow import com.squareup.workflow.ui.compose.tooling.preview +import com.squareup.workflow1.Sink +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** * A [ComposeWorkflow] that is used by [HelloWorkflow] to render the screen. diff --git a/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloWorkflow.kt b/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloWorkflow.kt index 62f7cfba..5b50661f 100644 --- a/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloWorkflow.kt +++ b/samples/src/main/java/com/squareup/sample/hellocomposerendering/HelloWorkflow.kt @@ -18,12 +18,11 @@ package com.squareup.sample.hellocomposerendering import com.squareup.sample.hellocomposerendering.HelloWorkflow.State import com.squareup.sample.hellocomposerendering.HelloWorkflow.State.Goodbye import com.squareup.sample.hellocomposerendering.HelloWorkflow.State.Hello -import com.squareup.workflow.RenderContext -import com.squareup.workflow.Snapshot -import com.squareup.workflow.StatefulWorkflow -import com.squareup.workflow.action -import com.squareup.workflow.parse import com.squareup.workflow.ui.compose.ComposeRendering +import com.squareup.workflow1.Snapshot +import com.squareup.workflow1.StatefulWorkflow +import com.squareup.workflow1.action +import com.squareup.workflow1.parse object HelloWorkflow : StatefulWorkflow() { enum class State { @@ -37,7 +36,7 @@ object HelloWorkflow : StatefulWorkflow( } private val helloAction = action { - nextState = nextState.theOtherState() + state = state.theOtherState() } override fun initialState( @@ -49,7 +48,7 @@ object HelloWorkflow : StatefulWorkflow( override fun render( props: Unit, state: State, - context: RenderContext + context: RenderContext ): ComposeRendering = context.renderChild(HelloRenderingWorkflow, state.name) { helloAction } diff --git a/samples/src/main/java/com/squareup/sample/nestedrenderings/LegacyRunner.kt b/samples/src/main/java/com/squareup/sample/nestedrenderings/LegacyRunner.kt index 0bd69959..039c7e8a 100644 --- a/samples/src/main/java/com/squareup/sample/nestedrenderings/LegacyRunner.kt +++ b/samples/src/main/java/com/squareup/sample/nestedrenderings/LegacyRunner.kt @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.sample.nestedrenderings import androidx.compose.foundation.layout.fillMaxSize @@ -21,11 +23,12 @@ import androidx.compose.ui.Modifier import androidx.ui.tooling.preview.Preview import com.squareup.sample.databinding.LegacyViewBinding import com.squareup.sample.nestedrenderings.RecursiveWorkflow.LegacyRendering -import com.squareup.workflow.ui.LayoutRunner -import com.squareup.workflow.ui.LayoutRunner.Companion.bind -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewFactory import com.squareup.workflow.ui.compose.tooling.preview +import com.squareup.workflow1.ui.LayoutRunner +import com.squareup.workflow1.ui.LayoutRunner.Companion.bind +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** * A [LayoutRunner] that renders [LegacyRendering]s using the legacy view framework. diff --git a/samples/src/main/java/com/squareup/sample/nestedrenderings/NestedRenderingsActivity.kt b/samples/src/main/java/com/squareup/sample/nestedrenderings/NestedRenderingsActivity.kt index e331e054..558cfc21 100644 --- a/samples/src/main/java/com/squareup/sample/nestedrenderings/NestedRenderingsActivity.kt +++ b/samples/src/main/java/com/squareup/sample/nestedrenderings/NestedRenderingsActivity.kt @@ -13,27 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.sample.nestedrenderings import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.compose.runtime.Providers import androidx.compose.ui.graphics.Color -import com.squareup.workflow.diagnostic.SimpleLoggingDiagnosticListener -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewRegistry -import com.squareup.workflow.ui.WorkflowRunner import com.squareup.workflow.ui.compose.withCompositionRoot -import com.squareup.workflow.ui.setContentWorkflow +import com.squareup.workflow1.SimpleLoggingWorkflowInterceptor +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowRunner +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi +import com.squareup.workflow1.ui.setContentWorkflow private val viewRegistry = ViewRegistry( RecursiveViewFactory, LegacyRunner ) -private val viewEnvironment = ViewEnvironment(viewRegistry).withCompositionRoot { content -> - Providers(BackgroundColorAmbient provides Color.Green, children = content) -} +private val viewEnvironment = ViewEnvironment(mapOf(ViewRegistry to viewRegistry)) + .withCompositionRoot { content -> + Providers(BackgroundColorAmbient provides Color.Green, children = content) + } class NestedRenderingsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -41,7 +45,7 @@ class NestedRenderingsActivity : AppCompatActivity() { setContentWorkflow(viewEnvironment) { WorkflowRunner.Config( RecursiveWorkflow, - diagnosticListener = SimpleLoggingDiagnosticListener() + interceptors = listOf(SimpleLoggingWorkflowInterceptor()) ) } } diff --git a/samples/src/main/java/com/squareup/sample/nestedrenderings/RecursiveViewFactory.kt b/samples/src/main/java/com/squareup/sample/nestedrenderings/RecursiveViewFactory.kt index 4216ad97..d87a1876 100644 --- a/samples/src/main/java/com/squareup/sample/nestedrenderings/RecursiveViewFactory.kt +++ b/samples/src/main/java/com/squareup/sample/nestedrenderings/RecursiveViewFactory.kt @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) @file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry") package com.squareup.sample.nestedrenderings -import androidx.compose.material.Text import androidx.compose.foundation.layout.Arrangement.SpaceEvenly import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayout @@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.Button import androidx.compose.material.Card +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.Providers import androidx.compose.runtime.ambientOf @@ -40,10 +41,11 @@ import androidx.compose.ui.res.dimensionResource import androidx.ui.tooling.preview.Preview import com.squareup.sample.R import com.squareup.sample.nestedrenderings.RecursiveWorkflow.Rendering -import com.squareup.workflow.ui.ViewEnvironment import com.squareup.workflow.ui.compose.WorkflowRendering import com.squareup.workflow.ui.compose.composedViewFactory import com.squareup.workflow.ui.compose.tooling.preview +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** * Ambient of [Color] to use as the background color for a [RecursiveViewFactory]. diff --git a/samples/src/main/java/com/squareup/sample/nestedrenderings/RecursiveWorkflow.kt b/samples/src/main/java/com/squareup/sample/nestedrenderings/RecursiveWorkflow.kt index c8780445..771922b2 100644 --- a/samples/src/main/java/com/squareup/sample/nestedrenderings/RecursiveWorkflow.kt +++ b/samples/src/main/java/com/squareup/sample/nestedrenderings/RecursiveWorkflow.kt @@ -18,11 +18,10 @@ package com.squareup.sample.nestedrenderings import com.squareup.sample.nestedrenderings.RecursiveWorkflow.LegacyRendering import com.squareup.sample.nestedrenderings.RecursiveWorkflow.Rendering import com.squareup.sample.nestedrenderings.RecursiveWorkflow.State -import com.squareup.workflow.RenderContext -import com.squareup.workflow.Snapshot -import com.squareup.workflow.StatefulWorkflow -import com.squareup.workflow.action -import com.squareup.workflow.renderChild +import com.squareup.workflow1.Snapshot +import com.squareup.workflow1.StatefulWorkflow +import com.squareup.workflow1.action +import com.squareup.workflow1.renderChild /** * A simple workflow that produces [Rendering]s of zero or more children. @@ -62,7 +61,7 @@ object RecursiveWorkflow : StatefulWorkflow() { override fun render( props: Unit, state: State, - context: RenderContext + context: RenderContext ): Rendering { return Rendering( children = List(state.children) { i -> @@ -74,13 +73,13 @@ object RecursiveWorkflow : StatefulWorkflow() { ) } - override fun snapshotState(state: State): Snapshot = Snapshot.EMPTY + override fun snapshotState(state: State): Snapshot? = null private fun addChild() = action { - nextState = nextState.copy(children = nextState.children + 1) + state = state.copy(children = state.children + 1) } private fun reset() = action { - nextState = State() + state = State() } } diff --git a/samples/src/main/java/com/squareup/sample/textinput/App.kt b/samples/src/main/java/com/squareup/sample/textinput/App.kt index 9ffa0baa..1aee0f68 100644 --- a/samples/src/main/java/com/squareup/sample/textinput/App.kt +++ b/samples/src/main/java/com/squareup/sample/textinput/App.kt @@ -13,24 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(WorkflowUiExperimentalApi::class) + package com.squareup.sample.textinput import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.ui.tooling.preview.Preview -import com.squareup.workflow.diagnostic.SimpleLoggingDiagnosticListener -import com.squareup.workflow.ui.ViewEnvironment -import com.squareup.workflow.ui.ViewRegistry import com.squareup.workflow.ui.compose.WorkflowContainer +import com.squareup.workflow1.SimpleLoggingWorkflowInterceptor +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewRegistry +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi private val viewRegistry = ViewRegistry(TextInputViewFactory) -private val viewEnvironment = ViewEnvironment(viewRegistry) +private val viewEnvironment = ViewEnvironment(mapOf(ViewRegistry to viewRegistry)) @Composable fun TextInputApp() { MaterialTheme { WorkflowContainer( TextInputWorkflow, viewEnvironment, - diagnosticListener = SimpleLoggingDiagnosticListener() + interceptors = listOf(SimpleLoggingWorkflowInterceptor()) ) } } diff --git a/samples/src/main/java/com/squareup/sample/textinput/TextInputViewFactory.kt b/samples/src/main/java/com/squareup/sample/textinput/TextInputViewFactory.kt index f3bc5463..5550f0c8 100644 --- a/samples/src/main/java/com/squareup/sample/textinput/TextInputViewFactory.kt +++ b/samples/src/main/java/com/squareup/sample/textinput/TextInputViewFactory.kt @@ -16,7 +16,6 @@ package com.squareup.sample.textinput import androidx.compose.animation.animateContentSize -import androidx.compose.material.Text import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -24,6 +23,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.Button import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier diff --git a/samples/src/main/java/com/squareup/sample/textinput/TextInputWorkflow.kt b/samples/src/main/java/com/squareup/sample/textinput/TextInputWorkflow.kt index 4616f207..e91f1b84 100644 --- a/samples/src/main/java/com/squareup/sample/textinput/TextInputWorkflow.kt +++ b/samples/src/main/java/com/squareup/sample/textinput/TextInputWorkflow.kt @@ -17,10 +17,9 @@ package com.squareup.sample.textinput import com.squareup.sample.textinput.TextInputWorkflow.Rendering import com.squareup.sample.textinput.TextInputWorkflow.State -import com.squareup.workflow.RenderContext -import com.squareup.workflow.Snapshot -import com.squareup.workflow.StatefulWorkflow -import com.squareup.workflow.action +import com.squareup.workflow1.Snapshot +import com.squareup.workflow1.StatefulWorkflow +import com.squareup.workflow1.action object TextInputWorkflow : StatefulWorkflow() { @@ -37,14 +36,14 @@ object TextInputWorkflow : StatefulWorkflow() { ) private val swapText = action { - nextState = nextState.copy(showingTextA = !nextState.showingTextA) + state = state.copy(showingTextA = !state.showingTextA) } private fun changeText(text: String) = action { - nextState = if (nextState.showingTextA) { - nextState.copy(textA = text) + state = if (state.showingTextA) { + state.copy(textA = text) } else { - nextState.copy(textB = text) + state.copy(textB = text) } } @@ -56,12 +55,12 @@ object TextInputWorkflow : StatefulWorkflow() { override fun render( props: Unit, state: State, - context: RenderContext + context: RenderContext ): Rendering = Rendering( text = if (state.showingTextA) state.textA else state.textB, onTextChanged = { context.actionSink.send(changeText(it)) }, onSwapText = { context.actionSink.send(swapText) } ) - override fun snapshotState(state: State): Snapshot = Snapshot.EMPTY + override fun snapshotState(state: State): Snapshot? = null } diff --git a/samples/src/main/res/layout/legacy_view.xml b/samples/src/main/res/layout/legacy_view.xml index e7afd621..c1b22fdc 100644 --- a/samples/src/main/res/layout/legacy_view.xml +++ b/samples/src/main/res/layout/legacy_view.xml @@ -27,7 +27,7 @@ android:textAlignment="center" /> -