diff --git a/samples/compose-samples/src/main/java/com/squareup/sample/compose/hellocomposebinding/HelloBindingActivity.kt b/samples/compose-samples/src/main/java/com/squareup/sample/compose/hellocomposebinding/HelloBindingActivity.kt index 6ced247b36..e175ac0480 100644 --- a/samples/compose-samples/src/main/java/com/squareup/sample/compose/hellocomposebinding/HelloBindingActivity.kt +++ b/samples/compose-samples/src/main/java/com/squareup/sample/compose/hellocomposebinding/HelloBindingActivity.kt @@ -17,7 +17,6 @@ import com.squareup.workflow1.ui.ViewEnvironment import com.squareup.workflow1.ui.ViewRegistry import com.squareup.workflow1.ui.WorkflowLayout import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.compose.composeViewFactory import com.squareup.workflow1.ui.compose.withCompositionRoot import com.squareup.workflow1.ui.plus import com.squareup.workflow1.ui.renderWorkflowIn @@ -31,7 +30,8 @@ private val viewEnvironment = } /** - * Demonstrates how to create and display a view factory with [composeViewFactory]. + * Demonstrates how to create and display a view factory with + * [composeScreenViewFactory][com.squareup.workflow1.ui.compose.composeScreenViewFactory]. */ class HelloBindingActivity : AppCompatActivity() { @OptIn(WorkflowUiExperimentalApi::class) diff --git a/samples/containers/hello-back-button/build.gradle.kts b/samples/containers/hello-back-button/build.gradle.kts index 8eaee04317..6cd6a71184 100644 --- a/samples/containers/hello-back-button/build.gradle.kts +++ b/samples/containers/hello-back-button/build.gradle.kts @@ -19,7 +19,6 @@ dependencies { implementation(libs.androidx.activity.ktx) implementation(project(":samples:containers:android")) - implementation(project(":workflow-ui:container-android")) implementation(project(":workflow-ui:core-android")) implementation(project(":workflow-ui:core-common")) } diff --git a/samples/dungeon/app/build.gradle.kts b/samples/dungeon/app/build.gradle.kts index 3325a1e00c..0c15f339a6 100644 --- a/samples/dungeon/app/build.gradle.kts +++ b/samples/dungeon/app/build.gradle.kts @@ -48,8 +48,6 @@ dependencies { implementation(project(":samples:dungeon:timemachine")) implementation(project(":samples:dungeon:timemachine-shakeable")) implementation(project(":workflow-tracing")) - implementation(project(":workflow-ui:container-android")) - implementation(project(":workflow-ui:container-common")) implementation(project(":workflow-ui:core-android")) implementation(project(":workflow-ui:core-common")) diff --git a/samples/dungeon/app/src/main/java/com/squareup/sample/dungeon/TimeMachineAppWorkflow.kt b/samples/dungeon/app/src/main/java/com/squareup/sample/dungeon/TimeMachineAppWorkflow.kt index 2f777544c0..f2f40ee584 100644 --- a/samples/dungeon/app/src/main/java/com/squareup/sample/dungeon/TimeMachineAppWorkflow.kt +++ b/samples/dungeon/app/src/main/java/com/squareup/sample/dungeon/TimeMachineAppWorkflow.kt @@ -7,10 +7,8 @@ import com.squareup.sample.timemachine.shakeable.ShakeableTimeMachineScreen import com.squareup.sample.timemachine.shakeable.ShakeableTimeMachineWorkflow import com.squareup.sample.timemachine.shakeable.ShakeableTimeMachineWorkflow.PropsFactory import com.squareup.workflow1.StatelessWorkflow -import com.squareup.workflow1.mapRendering import com.squareup.workflow1.renderChild import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.asScreen import kotlin.time.ExperimentalTime import kotlin.time.TimeSource @@ -25,10 +23,9 @@ class TimeMachineAppWorkflow( context: Context ) : StatelessWorkflow() { - @Suppress("DEPRECATION") private val timeMachineWorkflow = ShakeableTimeMachineWorkflow( - TimeMachineWorkflow(appWorkflow.mapRendering { asScreen(it) }, clock), + TimeMachineWorkflow(appWorkflow, clock), context ) diff --git a/samples/tictactoe/app/build.gradle.kts b/samples/tictactoe/app/build.gradle.kts index 2605da9c5d..3bf58b7d74 100644 --- a/samples/tictactoe/app/build.gradle.kts +++ b/samples/tictactoe/app/build.gradle.kts @@ -40,7 +40,6 @@ dependencies { implementation(project(":samples:containers:android")) implementation(project(":samples:tictactoe:common")) implementation(project(":workflow-tracing")) - implementation(project(":workflow-ui:container-android")) implementation(project(":workflow-ui:core-android")) implementation(project(":workflow-ui:core-common")) } diff --git a/settings.gradle.kts b/settings.gradle.kts index e3c4927996..d5e2c1eb08 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -73,8 +73,6 @@ include( ":workflow-ui:core-android", ":workflow-ui:internal-testing-android", ":workflow-ui:internal-testing-compose", - ":workflow-ui:container-common", - ":workflow-ui:container-android", ":workflow-ui:radiography" ) diff --git a/workflow-testing/src/test/java/com/squareup/workflow1/WorkerCompositionIntegrationTest.kt b/workflow-testing/src/test/java/com/squareup/workflow1/WorkerCompositionIntegrationTest.kt index cf524f923c..e5eaac5ecc 100644 --- a/workflow-testing/src/test/java/com/squareup/workflow1/WorkerCompositionIntegrationTest.kt +++ b/workflow-testing/src/test/java/com/squareup/workflow1/WorkerCompositionIntegrationTest.kt @@ -192,7 +192,7 @@ internal class WorkerCompositionIntegrationTest { state += 1 } - val workflow = Workflow.stateful Unit>( + val workflow = Workflow.stateful( initialState = 0, render = { _ -> runningWorker(triggerOutput) { action { setOutput(state) } } @@ -221,7 +221,7 @@ internal class WorkerCompositionIntegrationTest { @Test fun `runningWorker throws when output emitted`() { @Suppress("UNCHECKED_CAST") - val worker = Worker.from { Unit } as Worker + val worker = Worker.from { } as Worker val workflow = Workflow.stateless { // Suppress runningWorker usage because we want to make sure the deprecated // method still throws an exception as expected diff --git a/workflow-testing/src/test/java/com/squareup/workflow1/testing/RealRenderTesterTest.kt b/workflow-testing/src/test/java/com/squareup/workflow1/testing/RealRenderTesterTest.kt index 71da1d0506..667b51294b 100644 --- a/workflow-testing/src/test/java/com/squareup/workflow1/testing/RealRenderTesterTest.kt +++ b/workflow-testing/src/test/java/com/squareup/workflow1/testing/RealRenderTesterTest.kt @@ -984,7 +984,7 @@ internal class RealRenderTesterTest { @Test fun `verifyAction verifies workflow output`() { val child = Workflow.stateless {} - val workflow = Workflow.stateless { + val workflow = Workflow.stateless { renderChild(child) { TestAction(it) } } val testResult = workflow.testRender(Unit) @@ -1003,7 +1003,7 @@ internal class RealRenderTesterTest { @Test fun `verifyAction verifies worker output`() { val worker = Worker.finished() - val workflow = Workflow.stateless { + val workflow = Workflow.stateless { runningWorker(worker) { TestAction(it) } } val testResult = workflow.testRender(Unit) diff --git a/workflow-ui/compose-tooling/api/compose-tooling.api b/workflow-ui/compose-tooling/api/compose-tooling.api index 4bd4f750a5..a3f8dd69ad 100644 --- a/workflow-ui/compose-tooling/api/compose-tooling.api +++ b/workflow-ui/compose-tooling/api/compose-tooling.api @@ -1,10 +1,3 @@ -public final class com/squareup/workflow1/ui/compose/tooling/ComposableSingletons$LegacyViewFactoriesKt { - public static final field INSTANCE Lcom/squareup/workflow1/ui/compose/tooling/ComposableSingletons$LegacyViewFactoriesKt; - public static field lambda-1 Lkotlin/jvm/functions/Function4; - public fun ()V - public final fun getLambda-1$wf1_compose_tooling ()Lkotlin/jvm/functions/Function4; -} - public final class com/squareup/workflow1/ui/compose/tooling/ComposableSingletons$ViewFactoriesKt { public static final field INSTANCE Lcom/squareup/workflow1/ui/compose/tooling/ComposableSingletons$ViewFactoriesKt; public static field lambda-1 Lkotlin/jvm/functions/Function4; @@ -12,10 +5,6 @@ public final class com/squareup/workflow1/ui/compose/tooling/ComposableSingleton public final fun getLambda-1$wf1_compose_tooling ()Lkotlin/jvm/functions/Function4; } -public final class com/squareup/workflow1/ui/compose/tooling/LegacyViewFactoriesKt { - 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 -} - public final class com/squareup/workflow1/ui/compose/tooling/ViewFactoriesKt { public static final fun Preview (Lcom/squareup/workflow1/ui/ScreenViewFactory;Lcom/squareup/workflow1/ui/Screen;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V } diff --git a/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/LegacyPreviewViewFactoryTest.kt b/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/LegacyPreviewViewFactoryTest.kt deleted file mode 100644 index 0cb79e794a..0000000000 --- a/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/LegacyPreviewViewFactoryTest.kt +++ /dev/null @@ -1,181 +0,0 @@ -@file:Suppress("TestFunctionName", "PrivatePropertyName", "DEPRECATION") - -package com.squareup.workflow1.ui.compose.tooling - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertIsNotDisplayed -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.squareup.workflow1.ui.ViewEnvironmentKey -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.compose.WorkflowRendering -import com.squareup.workflow1.ui.compose.composeViewFactory -import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule -import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule -import leakcanary.DetectLeaksAfterTestSuccess -import org.junit.Rule -import org.junit.Test -import org.junit.rules.RuleChain -import org.junit.runner.RunWith - -@OptIn(WorkflowUiExperimentalApi::class) -@RunWith(AndroidJUnit4::class) -internal class LegacyPreviewViewFactoryTest { - - private val composeRule = createComposeRule() - - @get:Rule val rules: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess()) - .around(IdleAfterTestRule) - .around(composeRule) - .around(IdlingDispatcherRule) - - @Test fun singleChild() { - composeRule.setContent { - ParentWithOneChildPreview() - } - - composeRule.onNodeWithText("one").assertIsDisplayed() - composeRule.onNodeWithText("two").assertIsDisplayed() - } - - @Test fun twoChildren() { - composeRule.setContent { - ParentWithTwoChildrenPreview() - } - - composeRule.onNodeWithText("one").assertIsDisplayed() - composeRule.onNodeWithText("two").assertIsDisplayed() - composeRule.onNodeWithText("three").assertIsDisplayed() - } - - @Test fun recursive() { - composeRule.setContent { - ParentRecursivePreview() - } - - composeRule.onNodeWithText("one").assertIsDisplayed() - composeRule.onNodeWithText("two").assertIsDisplayed() - composeRule.onNodeWithText("three").assertIsDisplayed() - } - - @Test fun modifierIsApplied() { - composeRule.setContent { - ParentWithModifier() - } - - // The view factory will be rendered with size (0,0), so it should be reported as not displayed. - composeRule.onNodeWithText("one").assertIsNotDisplayed() - composeRule.onNodeWithText("two").assertIsNotDisplayed() - } - - @Test fun placeholderModifierIsApplied() { - composeRule.setContent { - ParentWithPlaceholderModifier() - } - - // The child will be rendered with size (0,0), so it should be reported as not displayed. - composeRule.onNodeWithText("one").assertIsDisplayed() - composeRule.onNodeWithText("two").assertIsNotDisplayed() - } - - @Test fun customViewEnvironment() { - composeRule.setContent { - ParentConsumesCustomKeyPreview() - } - - composeRule.onNodeWithText("foo").assertIsDisplayed() - } - - private val ParentWithOneChild = - composeViewFactory> { rendering, environment -> - Column { - BasicText(rendering.first) - WorkflowRendering(rendering.second, environment) - } - } - - @Preview @Composable - private fun ParentWithOneChildPreview() { - ParentWithOneChild.Preview(Pair("one", "two")) - } - - private val ParentWithTwoChildren = - composeViewFactory> { rendering, environment -> - Column { - WorkflowRendering(rendering.first, environment) - BasicText(rendering.second) - WorkflowRendering(rendering.third, environment) - } - } - - @Preview @Composable - private fun ParentWithTwoChildrenPreview() { - ParentWithTwoChildren.Preview(Triple("one", "two", "three")) - } - - data class RecursiveRendering( - val text: String, - val child: RecursiveRendering? = null - ) - - private val ParentRecursive = composeViewFactory { rendering, environment -> - Column { - BasicText(rendering.text) - rendering.child?.let { child -> - WorkflowRendering(rendering = child, viewEnvironment = environment) - } - } - } - - @Preview @Composable - private fun ParentRecursivePreview() { - ParentRecursive.Preview( - RecursiveRendering( - text = "one", - child = RecursiveRendering( - text = "two", - child = RecursiveRendering(text = "three") - ) - ) - ) - } - - @Preview @Composable - private fun ParentWithModifier() { - ParentWithOneChild.Preview( - Pair("one", "two"), - modifier = Modifier.size(0.dp) - ) - } - - @Preview @Composable - private fun ParentWithPlaceholderModifier() { - ParentWithOneChild.Preview( - Pair("one", "two"), - placeholderModifier = Modifier.size(0.dp) - ) - } - - object TestEnvironmentKey : ViewEnvironmentKey() { - override val default: String get() = error("Not specified") - } - - private val ParentConsumesCustomKey = composeViewFactory { _, environment -> - BasicText(environment[TestEnvironmentKey]) - } - - @Preview @Composable - private fun ParentConsumesCustomKeyPreview() { - ParentConsumesCustomKey.Preview(Unit) { - it + (TestEnvironmentKey to "foo") - } - } -} diff --git a/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/LegacyViewFactories.kt b/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/LegacyViewFactories.kt deleted file mode 100644 index 36d6a4d1f5..0000000000 --- a/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/LegacyViewFactories.kt +++ /dev/null @@ -1,73 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.compose.tooling - -import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -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.compose.WorkflowRendering -import com.squareup.workflow1.ui.compose.composeViewFactory - -/** - * Draws this [ViewFactory] using a special preview [ViewRegistry]. - * - * Use inside `@Preview` Composable functions. - * - * *Note: [rendering] must be the same type as this [ViewFactory], even though the type system does - * not enforce this constraint. This is due to a Compose compiler bug tracked - * [here](https://issuetracker.google.com/issues/156527332).* - * - * @param modifier [Modifier] that will be applied to this [ViewFactory]. - * @param placeholderModifier [Modifier] that will be applied to any nested renderings this factory - * shows. - * @param viewEnvironmentUpdater Function that configures the [ViewEnvironment] passed to this - * factory. - */ -@Deprecated("Use ScreenViewFactory.Preview") -@WorkflowUiExperimentalApi -@Composable -public fun ViewFactory.Preview( - rendering: RenderingT, - modifier: Modifier = Modifier, - placeholderModifier: Modifier = Modifier, - viewEnvironmentUpdater: ((ViewEnvironment) -> ViewEnvironment)? = null -) { - val previewEnvironment = - rememberPreviewViewEnvironment(placeholderModifier, viewEnvironmentUpdater, mainFactory = this) - WorkflowRendering(rendering, previewEnvironment, modifier) -} - -@OptIn(WorkflowUiExperimentalApi::class) -@Preview(showBackground = true) -@Composable -private fun ViewFactoryPreviewPreview() { - val factory = composeViewFactory { _, environment -> - Column( - verticalArrangement = spacedBy(8.dp), - modifier = Modifier.padding(8.dp) - ) { - BasicText("Top text") - WorkflowRendering( - rendering = "Child rendering with very long text to suss out cross-hatch rendering " + - "edge cases", - viewEnvironment = environment, - modifier = Modifier - .aspectRatio(1f) - .padding(8.dp) - ) - BasicText("Bottom text") - } - } - - factory.Preview(Unit) -} diff --git a/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/PlaceholderViewFactory.kt b/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/PlaceholderViewFactory.kt index 8eb53fd78b..e2b544b6f7 100644 --- a/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/PlaceholderViewFactory.kt +++ b/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/PlaceholderViewFactory.kt @@ -1,16 +1,11 @@ -@file:Suppress("SameParameterValue", "DEPRECATION") +@file:Suppress("SameParameterValue") @file:OptIn(WorkflowUiExperimentalApi::class) package com.squareup.workflow1.ui.compose.tooling -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.draw.drawBehind @@ -25,49 +20,12 @@ import androidx.compose.ui.graphics.drawscope.scale import androidx.compose.ui.graphics.withSaveLayer import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.squareup.workflow1.ui.AsScreen import com.squareup.workflow1.ui.Screen import com.squareup.workflow1.ui.ScreenViewFactory -import com.squareup.workflow1.ui.ViewFactory import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.compose.composeScreenViewFactory -import com.squareup.workflow1.ui.compose.composeViewFactory - -/** - * A [ViewFactory] that will be used any time a [PreviewViewRegistry] is asked to show a rendering. - * It displays a placeholder graphic and the rendering's `toString()` result. - */ -internal fun placeholderViewFactory(modifier: Modifier): ViewFactory = - composeViewFactory { rendering, _ -> - BoxWithConstraints { - BasicText( - modifier = modifier - .clipToBounds() - .drawBehind { - drawIntoCanvas { canvas -> - canvas.withSaveLayer(size.toRect(), Paint().apply { alpha = .2f }) { - canvas.drawRect(size.toRect(), Paint().apply { color = Color.Gray }) - drawCrossHatch( - color = Color.Red, - strokeWidth = 2.dp, - spaceWidth = 8.dp, - ) - } - } - } - .padding(8.dp), - text = rendering.toString(), - style = TextStyle( - textAlign = TextAlign.Center, - color = Color.White, - shadow = Shadow(blurRadius = 5f, color = Color.Black) - ) - ) - } - } /** * A [ScreenViewFactory] that will be used any time a [PreviewScreenViewFactoryFinder] @@ -93,7 +51,7 @@ internal fun placeholderScreenViewFactory(modifier: Modifier): ScreenViewFactory } } .padding(8.dp), - text = (rendering as? AsScreen<*>)?.rendering?.toString() ?: rendering.toString(), + text = rendering.toString(), style = TextStyle( textAlign = TextAlign.Center, color = Color.White, @@ -103,61 +61,6 @@ internal fun placeholderScreenViewFactory(modifier: Modifier): ScreenViewFactory } } -@Preview(widthDp = 200, heightDp = 200) -@Composable -private fun PreviewStubViewBindingOnWhite() { - Box(Modifier.background(Color.White)) { - PreviewStubBindingPreviewTemplate() - } -} - -@Preview(widthDp = 200, heightDp = 200) -@Composable -private fun PreviewStubViewBindingOnBlack() { - Box(Modifier.background(Color.Black)) { - PreviewStubBindingPreviewTemplate() - } -} - -@Preview(widthDp = 50, showBackground = true) -@Composable -private fun PreviewStubViewBindingTall() { - Box { - PreviewStubBindingPreviewTemplate("very long text to test cross-hatch rendering edge cases") - } -} - -@Preview(widthDp = 200, showBackground = true) -@Composable -private fun PreviewStubViewBindingWide() { - Box { - PreviewStubBindingPreviewTemplate("very long text to test cross-hatch rendering edge cases") - } -} - -@Composable private fun PreviewStubBindingPreviewTemplate(previewRendering: String = "preview") { - placeholderViewFactory(Modifier).Preview( - rendering = PlaceholderRendering(previewRendering), - placeholderModifier = Modifier - .fillMaxSize() - .border(width = 1.dp, color = Color.Red) - ) -} - -private class PlaceholderRendering(val text: String = "preview") : Screen { - override fun equals(other: Any?): Boolean { - return (other as? PlaceholderRendering)?.text == text - } - - override fun hashCode(): Int { - return text.hashCode() - } - - override fun toString(): String { - return text - } -} - private fun DrawScope.drawCrossHatch( color: Color, strokeWidth: Dp, diff --git a/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/PreviewViewEnvironment.kt b/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/PreviewViewEnvironment.kt index e89c552bcb..6201160773 100644 --- a/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/PreviewViewEnvironment.kt +++ b/workflow-ui/compose-tooling/src/main/java/com/squareup/workflow1/ui/compose/tooling/PreviewViewEnvironment.kt @@ -1,4 +1,3 @@ -@file:Suppress("DEPRECATION") @file:OptIn(WorkflowUiExperimentalApi::class) package com.squareup.workflow1.ui.compose.tooling @@ -11,36 +10,14 @@ import com.squareup.workflow1.ui.Screen import com.squareup.workflow1.ui.ScreenViewFactory import com.squareup.workflow1.ui.ScreenViewFactoryFinder 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 -import kotlin.reflect.KClass - -@Deprecated("Use overload with a ScreenViewFactory parameter.") -@Composable -internal fun rememberPreviewViewEnvironment( - placeholderModifier: Modifier, - viewEnvironmentUpdater: ((ViewEnvironment) -> ViewEnvironment)? = null, - mainFactory: ViewFactory<*>? = null -): ViewEnvironment { - val viewRegistry = remember(mainFactory, placeholderModifier) { - PreviewViewRegistry(mainFactory, placeholderViewFactory(placeholderModifier)) - } - return remember(viewRegistry, viewEnvironmentUpdater) { - (ViewEnvironment.EMPTY + viewRegistry).let { environment -> - // Give the preview a chance to add its own elements to the ViewEnvironment. - viewEnvironmentUpdater?.let { it(environment) } ?: environment - } - } -} /** * Creates and [remember]s a [ViewEnvironment] that has a special [ScreenViewFactoryFinder] * and any additional elements as configured by [viewEnvironmentUpdater]. * * The [ScreenViewFactoryFinder] will contain [mainFactory] if specified, as well as a - * [placeholderViewFactory] that will be used to show any renderings that don't match + * [placeholderScreenViewFactory] that will be used to show any renderings that don't match * [mainFactory]'s type. All placeholders will have [placeholderModifier] applied. */ @Composable internal fun rememberPreviewViewEnvironment( @@ -59,26 +36,6 @@ internal fun rememberPreviewViewEnvironment( } } -/** - * A [ViewRegistry] that uses [mainFactory] for rendering [RenderingT]s, and [placeholderFactory] - * for all other [WorkflowRendering][com.squareup.workflow1.ui.compose.WorkflowRendering] calls. - */ -@Immutable -private class PreviewViewRegistry( - private val mainFactory: ViewFactory? = null, - private val placeholderFactory: ViewFactory -) : ViewRegistry { - override val keys: Set> get() = mainFactory?.let { setOf(it.type) } ?: emptySet() - - @Suppress("UNCHECKED_CAST") - override fun getEntryFor( - renderingType: KClass - ): ViewFactory = when (renderingType) { - mainFactory?.type -> mainFactory - else -> placeholderFactory - } as ViewFactory -} - /** * A [ScreenViewFactoryFinder] that uses [mainFactory] for rendering [RenderingT]s, * and [placeholderFactory] for all other diff --git a/workflow-ui/compose/api/compose.api b/workflow-ui/compose/api/compose.api index 3065c35e80..eede71a594 100644 --- a/workflow-ui/compose/api/compose.api +++ b/workflow-ui/compose/api/compose.api @@ -1,16 +1,3 @@ -public abstract interface class com/squareup/workflow1/ui/compose/ComposeRendering : com/squareup/workflow1/ui/AndroidViewRendering { - public abstract fun Content (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroidx/compose/runtime/Composer;I)V - public abstract fun getViewFactory ()Lcom/squareup/workflow1/ui/ViewFactory; -} - -public final class com/squareup/workflow1/ui/compose/ComposeRendering$DefaultImpls { - public static fun getViewFactory (Lcom/squareup/workflow1/ui/compose/ComposeRendering;)Lcom/squareup/workflow1/ui/ViewFactory; -} - -public final class com/squareup/workflow1/ui/compose/ComposeRenderingKt { - public static final fun ComposeRendering (Lkotlin/jvm/functions/Function3;)Lcom/squareup/workflow1/ui/compose/ComposeRendering; -} - public abstract interface class com/squareup/workflow1/ui/compose/ComposeScreen : com/squareup/workflow1/ui/AndroidScreen { public abstract fun Content (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroidx/compose/runtime/Composer;I)V public abstract fun getViewFactory ()Lcom/squareup/workflow1/ui/ScreenViewFactory; @@ -35,25 +22,9 @@ public final class com/squareup/workflow1/ui/compose/ComposeScreenViewFactoryKt public static final fun composeScreenViewFactory (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function4;)Lcom/squareup/workflow1/ui/ScreenViewFactory; } -public abstract class com/squareup/workflow1/ui/compose/ComposeViewFactory : com/squareup/workflow1/ui/ViewFactory { - public static final field $stable I - public fun ()V - public abstract fun Content (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroidx/compose/runtime/Composer;I)V - public final fun buildView (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; -} - -public final class com/squareup/workflow1/ui/compose/ComposeViewFactoryKt { - public static final fun composeViewFactory (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function4;)Lcom/squareup/workflow1/ui/ViewFactory; -} - public final class com/squareup/workflow1/ui/compose/CompositionRootKt { public static final fun withCompositionRoot (Lcom/squareup/workflow1/ui/ScreenViewFactoryFinder;Lkotlin/jvm/functions/Function3;)Lcom/squareup/workflow1/ui/ScreenViewFactoryFinder; 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/workflow1/ui/compose/LegacyWorkflowRenderingKt { - 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/workflow1/ui/compose/RenderAsStateKt { diff --git a/workflow-ui/compose/build.gradle.kts b/workflow-ui/compose/build.gradle.kts index f398d3efd5..ab5ef89908 100644 --- a/workflow-ui/compose/build.gradle.kts +++ b/workflow-ui/compose/build.gradle.kts @@ -37,7 +37,6 @@ dependencies { androidTestImplementation(libs.androidx.test.truth) androidTestImplementation(libs.kotlin.test.jdk) - androidTestImplementation(project(":workflow-ui:container-android")) androidTestImplementation(project(":workflow-ui:internal-testing-compose")) api(libs.androidx.compose.runtime) diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewFactoryTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeScreenViewFactoryTest.kt similarity index 99% rename from workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewFactoryTest.kt rename to workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeScreenViewFactoryTest.kt index eaf1e0ef37..27f7ab2cca 100644 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewFactoryTest.kt +++ b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeScreenViewFactoryTest.kt @@ -33,7 +33,7 @@ import org.junit.runner.RunWith @OptIn(WorkflowUiExperimentalApi::class) @RunWith(AndroidJUnit4::class) -internal class ComposeViewFactoryTest { +internal class ComposeScreenViewFactoryTest { private val composeRule = createComposeRule() diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewFactoryTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewFactoryTest.kt deleted file mode 100644 index 68f7c865dd..0000000000 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewFactoryTest.kt +++ /dev/null @@ -1,145 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.compose - -import android.content.Context -import android.widget.FrameLayout -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertTextEquals -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.viewinterop.AndroidView -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.ViewEnvironmentKey -import com.squareup.workflow1.ui.ViewRegistry -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.WorkflowViewStub -import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule -import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule -import com.squareup.workflow1.ui.plus -import leakcanary.DetectLeaksAfterTestSuccess -import org.junit.Rule -import org.junit.Test -import org.junit.rules.RuleChain -import org.junit.runner.RunWith - -@OptIn(WorkflowUiExperimentalApi::class) -@RunWith(AndroidJUnit4::class) -internal class LegacyComposeViewFactoryTest { - - private val composeRule = createComposeRule() - - @get:Rule val rules: RuleChain = - RuleChain.outerRule(DetectLeaksAfterTestSuccess()) - .around(IdleAfterTestRule) - .around(composeRule) - .around(IdlingDispatcherRule) - - @Test fun showsComposeContent() { - val viewFactory = composeViewFactory { _, _ -> - BasicText("Hello, world!") - } - val viewEnvironment = ViewEnvironment.EMPTY + ViewRegistry(viewFactory) - - composeRule.setContent { - AndroidView(::RootView) { - it.stub.update(Unit, viewEnvironment) - } - } - - composeRule.onNodeWithText("Hello, world!").assertIsDisplayed() - } - - @Test fun getsRenderingUpdates() { - val viewFactory = composeViewFactory { rendering, _ -> - BasicText(rendering, Modifier.testTag("text")) - } - val viewEnvironment = ViewEnvironment.EMPTY + ViewRegistry(viewFactory) - var rendering by mutableStateOf("hello") - - composeRule.setContent { - AndroidView(::RootView) { - it.stub.update(rendering, viewEnvironment) - } - } - composeRule.onNodeWithTag("text").assertTextEquals("hello") - - rendering = "world" - - composeRule.onNodeWithTag("text").assertTextEquals("world") - } - - @Test fun getsViewEnvironmentUpdates() { - val testEnvironmentKey = object : ViewEnvironmentKey() { - override val default: String get() = error("No default") - } - - val viewFactory = composeViewFactory { _, environment -> - val text = environment[testEnvironmentKey] - BasicText(text, Modifier.testTag("text")) - } - val viewRegistry = ViewRegistry(viewFactory) - var viewEnvironment by mutableStateOf( - ViewEnvironment.EMPTY + viewRegistry + (testEnvironmentKey to "hello") - ) - - composeRule.setContent { - AndroidView(::RootView) { - it.stub.update(Unit, viewEnvironment) - } - } - composeRule.onNodeWithTag("text").assertTextEquals("hello") - - viewEnvironment = viewEnvironment + (testEnvironmentKey to "world") - - composeRule.onNodeWithTag("text").assertTextEquals("world") - } - - @Test fun wrapsFactoryWithRoot() { - val wrapperText = mutableStateOf("one") - val viewEnvironment = ViewEnvironment.EMPTY + ViewRegistry(TestFactory) - .withCompositionRoot { content -> - Column { - BasicText(wrapperText.value) - content() - } - } - - composeRule.setContent { - AndroidView(::RootView) { - it.stub.update(TestRendering("two"), viewEnvironment) - } - } - - // Compose bug doesn't let us use assertIsDisplayed on older devices. - // See https://issuetracker.google.com/issues/157728188. - composeRule.onNodeWithText("one").assertExists() - composeRule.onNodeWithText("two").assertExists() - - wrapperText.value = "ENO" - - composeRule.onNodeWithText("ENO").assertExists() - composeRule.onNodeWithText("two").assertExists() - } - - private class RootView(context: Context) : FrameLayout(context) { - val stub = WorkflowViewStub(context).also(::addView) - } - - private data class TestRendering(val text: String) - - private companion object { - val TestFactory = composeViewFactory { rendering, _ -> - BasicText(rendering.text) - } - } -} diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewTreeIntegrationTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewTreeIntegrationTest.kt deleted file mode 100644 index 3120134bd6..0000000000 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewTreeIntegrationTest.kt +++ /dev/null @@ -1,634 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.compose - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import androidx.compose.foundation.clickable -import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnDetachedFromWindow -import androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertTextEquals -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.performClick -import com.google.common.truth.Truth.assertThat -import com.squareup.workflow1.ui.AndroidScreen -import com.squareup.workflow1.ui.AndroidViewRendering -import com.squareup.workflow1.ui.Compatible -import com.squareup.workflow1.ui.ScreenViewFactory -import com.squareup.workflow1.ui.ScreenViewHolder -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.backstack.BackStackScreen -import com.squareup.workflow1.ui.bindShowRendering -import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule -import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule -import com.squareup.workflow1.ui.internal.test.LegacyWorkflowUiTestActivity -import com.squareup.workflow1.ui.modal.HasModals -import com.squareup.workflow1.ui.modal.ModalViewContainer -import leakcanary.DetectLeaksAfterTestSuccess -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.rules.RuleChain -import kotlin.reflect.KClass - -@OptIn(WorkflowUiExperimentalApi::class) -internal class LegacyComposeViewTreeIntegrationTest { - - private val composeRule = createAndroidComposeRule() - - @get:Rule val rules: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess()) - .around(IdleAfterTestRule) - .around(composeRule) - .around(IdlingDispatcherRule) - - private val scenario get() = composeRule.activityRule.scenario - - @Before fun setUp() { - scenario.onActivity { - it.viewEnvironment = ViewEnvironment( - mapOf( - ViewRegistry to ViewRegistry( - ModalViewContainer.binding(), - // This now provides a glue binding to the non-legacy BackStackScreen. - com.squareup.workflow1.ui.backstack.BackStackContainer, - NoTransitionBackStackContainer, - ) - ) - ) - } - } - - @Test fun compose_view_assertions_work() { - val firstScreen = TestComposeRendering("first") { - BasicText("First Screen") - } - val secondScreen = TestComposeRendering("second") {} - - scenario.onActivity { - it.setBackstack(firstScreen) - } - - composeRule.onNodeWithText("First Screen").assertIsDisplayed() - - // Navigate away from the first screen. - scenario.onActivity { - it.setBackstack(firstScreen, secondScreen) - } - - composeRule.onNodeWithText("First Screen").assertDoesNotExist() - } - - @Test fun composition_is_disposed_when_navigated_away_dispose_on_detach_strategy() { - var composedCount = 0 - var disposedCount = 0 - val firstScreen = TestComposeRendering("first", disposeStrategy = DisposeOnDetachedFromWindow) { - DisposableEffect(Unit) { - composedCount++ - onDispose { - disposedCount++ - } - } - } - val secondScreen = TestComposeRendering("second") {} - - scenario.onActivity { - it.setBackstack(firstScreen) - } - - composeRule.runOnIdle { - assertThat(composedCount).isEqualTo(1) - assertThat(disposedCount).isEqualTo(0) - } - - // Navigate away. - scenario.onActivity { - it.setBackstack(firstScreen, secondScreen) - } - - composeRule.runOnIdle { - assertThat(composedCount).isEqualTo(1) - assertThat(disposedCount).isEqualTo(1) - } - } - - @Test fun composition_is_disposed_when_navigated_away_dispose_on_destroy_strategy() { - var composedCount = 0 - var disposedCount = 0 - val firstScreen = - TestComposeRendering("first", disposeStrategy = DisposeOnViewTreeLifecycleDestroyed) { - DisposableEffect(Unit) { - composedCount++ - onDispose { - disposedCount++ - } - } - } - val secondScreen = TestComposeRendering("second") {} - - scenario.onActivity { - it.setBackstack(firstScreen) - } - - composeRule.runOnIdle { - assertThat(composedCount).isEqualTo(1) - assertThat(disposedCount).isEqualTo(0) - } - - // Navigate away. - scenario.onActivity { - it.setBackstack(firstScreen, secondScreen) - } - - composeRule.runOnIdle { - assertThat(composedCount).isEqualTo(1) - assertThat(disposedCount).isEqualTo(1) - } - } - - @Test fun composition_state_is_restored_after_config_change() { - val firstScreen = TestComposeRendering("first") { - var counter by rememberSaveable { mutableStateOf(0) } - BasicText( - "Counter: $counter", - Modifier - .clickable { counter++ } - .testTag(CounterTag) - ) - } - - // Show first screen to initialize state. - scenario.onActivity { - it.setBackstack(firstScreen) - } - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 0") - .performClick() - .assertTextEquals("Counter: 1") - - // Simulate config change. - scenario.recreate() - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 1") - } - - @Test fun composition_state_is_restored_after_navigating_back() { - val firstScreen = TestComposeRendering("first") { - var counter by rememberSaveable { mutableStateOf(0) } - BasicText( - "Counter: $counter", - Modifier - .clickable { counter++ } - .testTag(CounterTag) - ) - } - val secondScreen = TestComposeRendering("second") { - BasicText("nothing to see here") - } - - // Show first screen to initialize state. - scenario.onActivity { - it.setBackstack(firstScreen) - } - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 0") - .performClick() - .assertTextEquals("Counter: 1") - - // Add a screen to the backstack. - scenario.onActivity { - it.setBackstack(firstScreen, secondScreen) - } - - composeRule.onNodeWithTag(CounterTag) - .assertDoesNotExist() - - // Navigate back. - scenario.onActivity { - it.setBackstack(firstScreen) - } - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 1") - } - - @Test - fun composition_state_is_restored_after_config_change_then_navigating_back() { - val firstScreen = TestComposeRendering("first") { - var counter by rememberSaveable { mutableStateOf(0) } - BasicText( - "Counter: $counter", - Modifier - .clickable { counter++ } - .testTag(CounterTag) - ) - } - val secondScreen = TestComposeRendering("second") { - BasicText("nothing to see here") - } - - // Show first screen to initialize state. - scenario.onActivity { - it.setBackstack(firstScreen) - } - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 0") - .performClick() - .assertTextEquals("Counter: 1") - - // Add a screen to the backstack. - scenario.onActivity { - it.setBackstack(firstScreen, secondScreen) - } - - scenario.recreate() - - composeRule.onNodeWithText("nothing to see here") - .assertIsDisplayed() - - // Navigate to the first screen again. - scenario.onActivity { - it.setBackstack(firstScreen) - } - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 1") - } - - @Test fun composition_state_is_not_restored_after_screen_is_removed_from_backstack() { - val firstScreen = TestComposeRendering("first") { - var counter by rememberSaveable { mutableStateOf(0) } - BasicText( - "Counter: $counter", - Modifier - .clickable { counter++ } - .testTag(CounterTag) - ) - } - val secondScreen = TestComposeRendering("second") { - BasicText("nothing to see here") - } - - // Show first screen to initialize state. - scenario.onActivity { - it.setBackstack(firstScreen) - } - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 0") - .performClick() - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 1") - - // Add a screen to the backstack. - scenario.onActivity { - it.setBackstack(firstScreen, secondScreen) - } - - // Remove the initial screen from the backstack – this should drop its state. - scenario.onActivity { - it.setBackstack(secondScreen) - } - - // Navigate to the first screen again. - scenario.onActivity { - it.setBackstack(firstScreen) - } - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 0") - } - - @Test - fun composition_state_is_not_restored_after_screen_is_removed_and_replaced_from_backstack() { - val firstScreen = TestComposeRendering("first") { - var counter by rememberSaveable { mutableStateOf(0) } - BasicText( - "Counter: $counter", - Modifier - .clickable { counter++ } - .testTag(CounterTag) - ) - } - val secondScreen = TestComposeRendering("second") { - BasicText("nothing to see here") - } - - // Show first screen to initialize state. - scenario.onActivity { - it.setBackstack(firstScreen) - } - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 0") - .performClick() - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 1") - - // Add a screen to the backstack. - scenario.onActivity { - it.setBackstack(firstScreen, secondScreen) - } - - // Remove the initial screen from the backstack – this should drop its state. - scenario.onActivity { - it.setBackstack(secondScreen) - } - - // Put the initial screen back – it should still not have saved state anymore. - scenario.onActivity { - it.setBackstack(firstScreen, secondScreen) - } - - // Navigate to the first screen again. - scenario.onActivity { - it.setBackstack(firstScreen) - } - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 0") - } - - @Test fun composition_is_restored_in_modal_after_config_change() { - val firstScreen = TestComposeRendering(compatibilityKey = "") { - var counter by rememberSaveable { mutableStateOf(0) } - BasicText( - "Counter: $counter", - Modifier - .clickable { counter++ } - .testTag(CounterTag) - ) - } - - // Show first screen to initialize state. - scenario.onActivity { - it.setRendering( - TestModalScreen( - listOf( - BackStackScreen(EmptyRendering, firstScreen) - ) - ) - ) - } - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 0") - .performClick() - .assertTextEquals("Counter: 1") - - scenario.recreate() - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 1") - } - - @Test fun composition_is_restored_in_multiple_modals_after_config_change() { - val firstScreen = TestComposeRendering(compatibilityKey = "key") { - var counter by rememberSaveable { mutableStateOf(0) } - BasicText( - "Counter: $counter", - Modifier - .clickable { counter++ } - .testTag(CounterTag) - ) - } - - // Use the same compatibility key – these screens are in different modals, so they won't - // conflict. - val secondScreen = TestComposeRendering(compatibilityKey = "key") { - var counter by rememberSaveable { mutableStateOf(0) } - BasicText( - "Counter2: $counter", - Modifier - .clickable { counter++ } - .testTag(CounterTag2) - ) - } - - // Use the same compatibility key – these screens are in different modals, so they won't - // conflict. - val thirdScreen = TestComposeRendering(compatibilityKey = "key") { - var counter by rememberSaveable { mutableStateOf(0) } - BasicText( - "Counter3: $counter", - Modifier - .clickable { counter++ } - .testTag(CounterTag3) - ) - } - - // Show first screen to initialize state. - scenario.onActivity { - it.setRendering( - TestModalScreen( - listOf( - firstScreen, - secondScreen, - thirdScreen - ) - ) - ) - } - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 0") - .performClick() - .assertTextEquals("Counter: 1") - - composeRule.onNodeWithTag(CounterTag2) - .assertTextEquals("Counter2: 0") - .performClick() - .assertTextEquals("Counter2: 1") - - composeRule.onNodeWithTag(CounterTag3) - .assertTextEquals("Counter3: 0") - .performClick() - .assertTextEquals("Counter3: 1") - - scenario.recreate() - - composeRule.onNodeWithTag(CounterTag) - .assertTextEquals("Counter: 1") - - composeRule.onNodeWithTag(CounterTag2) - .assertTextEquals("Counter2: 1") - - composeRule.onNodeWithTag(CounterTag3) - .assertTextEquals("Counter3: 1") - } - - @Test fun composition_is_restored_in_multiple_modals_backstacks_after_config_change() { - fun createRendering( - layer: Int, - screen: Int - ) = TestComposeRendering( - // Use the same compatibility key across layers – these screens are in different modals, so - // they won't conflict. - compatibilityKey = screen.toString() - ) { - var counter by rememberSaveable { mutableStateOf(0) } - BasicText( - "Counter[$layer][$screen]: $counter", - Modifier - .clickable { counter++ } - .testTag("L${layer}S$screen") - ) - } - - val layer0Screen0 = createRendering(0, 0) - val layer0Screen1 = createRendering(0, 1) - val layer1Screen0 = createRendering(1, 0) - val layer1Screen1 = createRendering(1, 1) - - // Show first screen to initialize state. - scenario.onActivity { - it.setRendering( - TestModalScreen( - listOf( - BackStackScreen(EmptyRendering, layer0Screen0), - BackStackScreen(EmptyRendering, layer1Screen0) - ) - ) - ) - } - - composeRule.onNodeWithTag("L0S0") - .assertTextEquals("Counter[0][0]: 0") - .assertIsDisplayed() - .performClick() - .assertTextEquals("Counter[0][0]: 1") - - composeRule.onNodeWithTag("L1S0") - .assertTextEquals("Counter[1][0]: 0") - .assertIsDisplayed() - .performClick() - .assertTextEquals("Counter[1][0]: 1") - - // Push some screens onto the backstack. - scenario.onActivity { - it.setRendering( - TestModalScreen( - listOf( - BackStackScreen(EmptyRendering, layer0Screen0, layer0Screen1), - BackStackScreen(EmptyRendering, layer1Screen0, layer1Screen1) - ) - ) - ) - } - - composeRule.onNodeWithTag("L0S0") - .assertDoesNotExist() - composeRule.onNodeWithTag("L1S0") - .assertDoesNotExist() - - composeRule.onNodeWithTag("L0S1") - .assertTextEquals("Counter[0][1]: 0") - .assertIsDisplayed() - .performClick() - .assertTextEquals("Counter[0][1]: 1") - - composeRule.onNodeWithTag("L1S1") - .assertTextEquals("Counter[1][1]: 0") - .assertIsDisplayed() - .performClick() - .assertTextEquals("Counter[1][1]: 1") - - // Simulate config change. - scenario.recreate() - - // Check that the last-shown screens were restored. - composeRule.onNodeWithTag("L0S1") - .assertIsDisplayed() - composeRule.onNodeWithTag("L1S1") - .assertIsDisplayed() - - // Pop both backstacks and check that screens were restored. - scenario.onActivity { - it.setRendering( - TestModalScreen( - listOf( - BackStackScreen(EmptyRendering, layer0Screen0), - BackStackScreen(EmptyRendering, layer1Screen0) - ) - ) - ) - } - - composeRule.onNodeWithText("Counter[0][0]: 1") - .assertIsDisplayed() - composeRule.onNodeWithText("Counter[1][0]: 1") - .assertIsDisplayed() - } - - private fun LegacyWorkflowUiTestActivity.setBackstack(vararg backstack: TestComposeRendering) { - setRendering(BackStackScreen(EmptyRendering, backstack.asList())) - } - - data class TestModalScreen( - override val modals: List = emptyList() - ) : HasModals { - override val beneathModals = EmptyRendering - } - - data class TestComposeRendering( - override val compatibilityKey: String, - val disposeStrategy: ViewCompositionStrategy? = null, - val content: @Composable () -> Unit - ) : Compatible, AndroidViewRendering, ViewFactory { - override val type: KClass = TestComposeRendering::class - override val viewFactory: ViewFactory get() = this - - override fun buildView( - initialRendering: TestComposeRendering, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View { - var lastCompositionStrategy = initialRendering.disposeStrategy - - return ComposeView(contextForNewView).apply { - lastCompositionStrategy?.let(::setViewCompositionStrategy) - - bindShowRendering(initialRendering, initialViewEnvironment) { rendering, _ -> - if (rendering.disposeStrategy != lastCompositionStrategy) { - lastCompositionStrategy = rendering.disposeStrategy - lastCompositionStrategy?.let(::setViewCompositionStrategy) - } - - setContent(rendering.content) - } - } - } - } - - object EmptyRendering : AndroidScreen { - override val viewFactory: ScreenViewFactory - get() = ScreenViewFactory.fromCode { _, e, c, _ -> - ScreenViewHolder(e, View(c)) { _, _, -> } - } - } - - companion object { - const val CounterTag = "counter" - const val CounterTag2 = "counter2" - const val CounterTag3 = "counter3" - } -} diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyWorkflowRenderingTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyWorkflowRenderingTest.kt deleted file mode 100644 index 70320e8293..0000000000 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyWorkflowRenderingTest.kt +++ /dev/null @@ -1,587 +0,0 @@ -@file:Suppress("TestFunctionName", "DEPRECATION") - -package com.squareup.workflow1.ui.compose - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.sizeIn -import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.test.assertHeightIsEqualTo -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertTextEquals -import androidx.compose.ui.test.assertWidthIsEqualTo -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.performClick -import androidx.compose.ui.unit.dp -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.Lifecycle.Event -import androidx.lifecycle.Lifecycle.Event.ON_CREATE -import androidx.lifecycle.Lifecycle.Event.ON_DESTROY -import androidx.lifecycle.Lifecycle.Event.ON_PAUSE -import androidx.lifecycle.Lifecycle.Event.ON_RESUME -import androidx.lifecycle.Lifecycle.Event.ON_START -import androidx.lifecycle.Lifecycle.Event.ON_STOP -import androidx.lifecycle.Lifecycle.State -import androidx.lifecycle.Lifecycle.State.CREATED -import androidx.lifecycle.Lifecycle.State.DESTROYED -import androidx.lifecycle.Lifecycle.State.INITIALIZED -import androidx.lifecycle.Lifecycle.State.RESUMED -import androidx.lifecycle.Lifecycle.State.STARTED -import androidx.lifecycle.LifecycleEventObserver -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.LifecycleRegistry -import androidx.lifecycle.findViewTreeLifecycleOwner -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.google.common.truth.Truth.assertThat -import com.squareup.workflow1.ui.AndroidViewRendering -import com.squareup.workflow1.ui.BuilderViewFactory -import com.squareup.workflow1.ui.Compatible -import com.squareup.workflow1.ui.Named -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.bindShowRendering -import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule -import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule -import com.squareup.workflow1.ui.plus -import leakcanary.DetectLeaksAfterTestSuccess -import org.hamcrest.Description -import org.hamcrest.TypeSafeMatcher -import org.junit.Rule -import org.junit.Test -import org.junit.rules.RuleChain -import org.junit.runner.RunWith -import kotlin.reflect.KClass - -@OptIn(WorkflowUiExperimentalApi::class) -@RunWith(AndroidJUnit4::class) -internal class LegacyWorkflowRenderingTest { - - private val composeRule = createComposeRule() - - @get:Rule val rules: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess()) - .around(IdleAfterTestRule) - .around(composeRule) - .around(IdlingDispatcherRule) - - @Test fun doesNotRecompose_whenFactoryChanged() { - val registry1 = ViewRegistry( - composeViewFactory { rendering, _ -> - BasicText(rendering) - } - ) - val registry2 = ViewRegistry( - composeViewFactory { rendering, _ -> - BasicText(rendering.reversed()) - } - ) - val registry = mutableStateOf(registry1) - - composeRule.setContent { - WorkflowRendering("hello", ViewEnvironment.EMPTY + registry.value) - } - - composeRule.onNodeWithText("hello").assertIsDisplayed() - registry.value = registry2 - composeRule.onNodeWithText("hello").assertIsDisplayed() - composeRule.onNodeWithText("olleh").assertDoesNotExist() - } - - /** - * Ensures we match the behavior of WorkflowViewStub and other containers, which only check for - * a new factory when a new rendering is incompatible with the current one. - */ - @Test fun doesNotRecompose_whenAndroidViewRendering_factoryChanged() { - data class ShiftyRendering(val whichFactory: Boolean) : AndroidViewRendering { - override val viewFactory: ViewFactory = when (whichFactory) { - true -> composeViewFactory { _, _ -> BasicText("one") } - false -> composeViewFactory { _, _ -> BasicText("two") } - } - } - - var rendering by mutableStateOf(ShiftyRendering(true)) - - composeRule.setContent { - WorkflowRendering(rendering, ViewEnvironment.EMPTY) - } - - composeRule.onNodeWithText("one").assertIsDisplayed() - rendering = ShiftyRendering(false) - composeRule.onNodeWithText("one").assertIsDisplayed() - } - - @Test fun wrapsFactoryWithRoot_whenAlreadyInComposition() { - data class TestRendering(val text: String) - - val testFactory = composeViewFactory { rendering, _ -> - BasicText(rendering.text) - } - val viewEnvironment = ViewEnvironment.EMPTY + ViewRegistry(testFactory) - .withCompositionRoot { content -> - Column { - BasicText("one") - content() - } - } - - composeRule.setContent { - WorkflowRendering(TestRendering("two"), viewEnvironment) - } - - composeRule.onNodeWithText("one").assertIsDisplayed() - composeRule.onNodeWithText("two").assertIsDisplayed() - } - - @Test fun legacyAndroidViewRendersUpdates() { - val wrapperText = mutableStateOf("two") - - composeRule.setContent { - WorkflowRendering(LegacyViewRendering(wrapperText.value), ViewEnvironment.EMPTY) - } - - onView(withText("two")).check(matches(isDisplayed())) - wrapperText.value = "OWT" - onView(withText("OWT")).check(matches(isDisplayed())) - } - - // https://github.com/square/workflow-kotlin/issues/538 - @Test fun includesSupportForNamed() { - val wrapperText = mutableStateOf("two") - - composeRule.setContent { - val rendering = Named(LegacyViewRendering(wrapperText.value), "fnord") - WorkflowRendering(rendering, ViewEnvironment.EMPTY) - } - - onView(withText("two")).check(matches(isDisplayed())) - wrapperText.value = "OWT" - onView(withText("OWT")).check(matches(isDisplayed())) - } - - @Test fun destroysChildLifecycle_fromCompose_whenIncompatibleRendering() { - val lifecycleEvents = mutableListOf() - - class LifecycleRecorder : ComposableRendering { - @Composable override fun Content(viewEnvironment: ViewEnvironment) { - val lifecycle = LocalLifecycleOwner.current.lifecycle - DisposableEffect(lifecycle) { - lifecycle.addObserver( - LifecycleEventObserver { _, event -> - lifecycleEvents += event - } - ) - onDispose { - // Yes, we're leaking the observer. That's intentional: we need to make sure we see any - // lifecycle events that happen even after the composable is destroyed. - } - } - } - } - - class EmptyRendering : ComposableRendering { - @Composable override fun Content(viewEnvironment: ViewEnvironment) {} - } - - var rendering: Any by mutableStateOf(LifecycleRecorder()) - composeRule.setContent { - WorkflowRendering(rendering, ViewEnvironment.EMPTY) - } - - composeRule.runOnIdle { - assertThat(lifecycleEvents).containsExactly(ON_CREATE, ON_START, ON_RESUME).inOrder() - lifecycleEvents.clear() - } - - rendering = EmptyRendering() - - composeRule.runOnIdle { - assertThat(lifecycleEvents).containsExactly(ON_PAUSE, ON_STOP, ON_DESTROY).inOrder() - } - } - - @Test fun destroysChildLifecycle_fromLegacyView_whenIncompatibleRendering() { - val lifecycleEvents = mutableListOf() - - class LifecycleRecorder : AndroidViewRendering { - override val viewFactory: ViewFactory = BuilderViewFactory( - LifecycleRecorder::class - ) { initialRendering, initialViewEnvironment, contextForNewView, _ -> - object : View(contextForNewView) { - init { - bindShowRendering(initialRendering, initialViewEnvironment) { _, _ -> } - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - val lifecycle = this.findViewTreeLifecycleOwner()!!.lifecycle - lifecycle.addObserver( - LifecycleEventObserver { _, event -> - lifecycleEvents += event - } - ) - // Yes, we're leaking the observer. That's intentional: we need to make sure we see - // any lifecycle events that happen even after the composable is destroyed. - } - } - } - } - - class EmptyRendering : ComposableRendering { - @Composable override fun Content(viewEnvironment: ViewEnvironment) {} - } - - var rendering: Any by mutableStateOf(LifecycleRecorder()) - composeRule.setContent { - WorkflowRendering(rendering, ViewEnvironment.EMPTY) - } - - composeRule.runOnIdle { - assertThat(lifecycleEvents).containsExactly(ON_CREATE, ON_START, ON_RESUME).inOrder() - lifecycleEvents.clear() - } - - rendering = EmptyRendering() - - composeRule.runOnIdle { - assertThat(lifecycleEvents).containsExactly(ON_PAUSE, ON_STOP, ON_DESTROY).inOrder() - } - } - - @Test fun followsParentLifecycle() { - val states = mutableListOf() - val parentOwner = object : LifecycleOwner { - val registry = LifecycleRegistry(this) - override val lifecycle: Lifecycle - get() = registry - } - - composeRule.setContent { - CompositionLocalProvider(LocalLifecycleOwner provides parentOwner) { - WorkflowRendering(LifecycleRecorder(states), ViewEnvironment.EMPTY) - } - } - - composeRule.runOnIdle { - assertThat(states).containsExactly(INITIALIZED).inOrder() - states.clear() - parentOwner.registry.currentState = STARTED - } - - composeRule.runOnIdle { - assertThat(states).containsExactly(CREATED, STARTED).inOrder() - states.clear() - parentOwner.registry.currentState = CREATED - } - - composeRule.runOnIdle { - assertThat(states).containsExactly(CREATED).inOrder() - states.clear() - parentOwner.registry.currentState = RESUMED - } - - composeRule.runOnIdle { - assertThat(states).containsExactly(STARTED, RESUMED).inOrder() - states.clear() - parentOwner.registry.currentState = DESTROYED - } - - composeRule.runOnIdle { - assertThat(states).containsExactly(STARTED, CREATED, DESTROYED).inOrder() - } - } - - @Test fun handlesParentInitiallyDestroyed() { - val states = mutableListOf() - val parentOwner = object : LifecycleOwner { - val registry = LifecycleRegistry(this) - override val lifecycle: Lifecycle - get() = registry - } - composeRule.runOnIdle { - // Cannot go directly to DESTROYED - parentOwner.registry.currentState = CREATED - parentOwner.registry.currentState = DESTROYED - } - - composeRule.setContent { - CompositionLocalProvider(LocalLifecycleOwner provides parentOwner) { - WorkflowRendering(LifecycleRecorder(states), ViewEnvironment.EMPTY) - } - } - - composeRule.runOnIdle { - assertThat(states).containsExactly(INITIALIZED).inOrder() - } - } - - @Test fun appliesModifierToComposableContent() { - class Rendering : ComposableRendering { - @Composable override fun Content(viewEnvironment: ViewEnvironment) { - Box( - Modifier - .testTag("box") - .fillMaxSize() - ) - } - } - - composeRule.setContent { - WorkflowRendering( - Rendering(), - ViewEnvironment.EMPTY, - Modifier.size(width = 42.dp, height = 43.dp) - ) - } - - composeRule.onNodeWithTag("box") - .assertWidthIsEqualTo(42.dp) - .assertHeightIsEqualTo(43.dp) - } - - @Test fun propagatesMinConstraints() { - class Rendering : ComposableRendering { - @Composable override fun Content(viewEnvironment: ViewEnvironment) { - Box(Modifier.testTag("box")) - } - } - - composeRule.setContent { - WorkflowRendering( - Rendering(), - ViewEnvironment.EMPTY, - Modifier.sizeIn(minWidth = 42.dp, minHeight = 43.dp) - ) - } - - composeRule.onNodeWithTag("box") - .assertWidthIsEqualTo(42.dp) - .assertHeightIsEqualTo(43.dp) - } - - @Test fun appliesModifierToViewContent() { - val viewId = View.generateViewId() - - class LegacyRendering(private val viewId: Int) : AndroidViewRendering { - override val viewFactory: ViewFactory = BuilderViewFactory( - LegacyRendering::class - ) { initialRendering, initialViewEnvironment, contextForNewView, _ -> - object : View(contextForNewView) { - init { - bindShowRendering(initialRendering, initialViewEnvironment) { r, _ -> - id = r.viewId - } - } - } - } - } - - composeRule.setContent { - with(LocalDensity.current) { - WorkflowRendering( - LegacyRendering(viewId), - ViewEnvironment.EMPTY, - Modifier.size(42.toDp(), 43.toDp()) - ) - } - } - - onView(withId(viewId)).check(matches(hasSize(42, 43))) - } - - @Test fun skipsPreviousContentWhenIncompatible() { - var disposeCount = 0 - - class Rendering( - override val compatibilityKey: String - ) : ComposableRendering, Compatible { - @Composable override fun Content(viewEnvironment: ViewEnvironment) { - var counter by rememberSaveable { mutableStateOf(0) } - Column { - BasicText( - "$compatibilityKey: $counter", - Modifier - .testTag("tag") - .clickable { counter++ } - ) - DisposableEffect(Unit) { - onDispose { - disposeCount++ - } - } - } - } - } - - var key by mutableStateOf("one") - composeRule.setContent { - WorkflowRendering(Rendering(key), ViewEnvironment.EMPTY) - } - - composeRule.onNodeWithTag("tag") - .assertTextEquals("one: 0") - .performClick() - .assertTextEquals("one: 1") - - key = "two" - - composeRule.onNodeWithTag("tag") - .assertTextEquals("two: 0") - composeRule.runOnIdle { - assertThat(disposeCount).isEqualTo(1) - } - - key = "one" - - // State should not be restored. - composeRule.onNodeWithTag("tag") - .assertTextEquals("one: 0") - composeRule.runOnIdle { - assertThat(disposeCount).isEqualTo(2) - } - } - - @Test fun doesNotSkipPreviousContentWhenCompatible() { - var disposeCount = 0 - - class Rendering(val text: String) : ComposableRendering { - @Composable override fun Content(viewEnvironment: ViewEnvironment) { - var counter by rememberSaveable { mutableStateOf(0) } - Column { - BasicText( - "$text: $counter", - Modifier - .testTag("tag") - .clickable { counter++ } - ) - DisposableEffect(Unit) { - onDispose { - disposeCount++ - } - } - } - } - } - - var text by mutableStateOf("one") - composeRule.setContent { - WorkflowRendering(Rendering(text), ViewEnvironment.EMPTY) - } - - composeRule.onNodeWithTag("tag") - .assertTextEquals("one: 0") - .performClick() - .assertTextEquals("one: 1") - - text = "two" - - // Counter state should be preserved. - composeRule.onNodeWithTag("tag") - .assertTextEquals("two: 1") - composeRule.runOnIdle { - assertThat(disposeCount).isEqualTo(0) - } - } - - @Suppress("SameParameterValue") - private fun hasSize( - width: Int, - height: Int - ) = object : TypeSafeMatcher() { - override fun describeTo(description: Description) { - description.appendText("has size ${width}x${height}px") - } - - override fun matchesSafely(item: View): Boolean { - return item.width == width && item.height == height - } - } - - private class LifecycleRecorder( - // For some reason, if we just capture the states val, it is null in the composable. - private val states: MutableList - ) : ComposableRendering { - @Composable override fun Content(viewEnvironment: ViewEnvironment) { - val lifecycle = LocalLifecycleOwner.current.lifecycle - DisposableEffect(lifecycle) { - this@LifecycleRecorder.states += lifecycle.currentState - lifecycle.addObserver( - LifecycleEventObserver { _, _ -> - this@LifecycleRecorder.states += lifecycle.currentState - } - ) - onDispose { - // Yes, we're leaking the observer. That's intentional: we need to make sure we see any - // lifecycle events that happen even after the composable is destroyed. - } - } - } - } - - private interface ComposableRendering> : - AndroidViewRendering { - - /** - * It is significant that this returns a new instance on every call, since we can't rely on real - * implementations in the wild to reuse the same factory instance across rendering instances. - */ - override val viewFactory: ViewFactory - get() = object : ComposeViewFactory>() { - override val type: KClass> = ComposableRendering::class - - @Composable override fun Content( - rendering: ComposableRendering<*>, - viewEnvironment: ViewEnvironment - ) { - rendering.Content(viewEnvironment) - } - } - - @Composable fun Content(viewEnvironment: ViewEnvironment) - } - - private data class LegacyViewRendering( - val text: String - ) : AndroidViewRendering { - override val viewFactory: ViewFactory = - object : ViewFactory { - override val type = LegacyViewRendering::class - - override fun buildView( - initialRendering: LegacyViewRendering, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View = TextView(contextForNewView).apply { - bindShowRendering(initialRendering, initialViewEnvironment) { rendering, _ -> - text = rendering.text - } - } - } - } -} diff --git a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/ComposeRendering.kt b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/ComposeRendering.kt deleted file mode 100644 index c873e2d988..0000000000 --- a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/ComposeRendering.kt +++ /dev/null @@ -1,88 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.compose - -import androidx.compose.runtime.Composable -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 -import kotlin.reflect.KClass - -/** - * Interface implemented by a rendering class to allow it to drive a composable UI via an - * appropriate [ComposeViewFactory] implementation, by simply overriding the [Content] method. - * This is the compose analog to [AndroidViewRendering]. - * - * Note that unlike most workflow view functions, [Content] does not take the rendering as a - * parameter. Instead, the rendering is the receiver, i.e. the current value of `this`. - * - * Example: - * - * ``` - * @OptIn(WorkflowUiExperimentalApi::class) - * data class HelloView( - * val message: String, - * val onClick: () -> Unit - * ) : ComposeRendering { - * - * @Composable override fun Content(viewEnvironment: ViewEnvironment) { - * Button(onClick) { - * Text(message) - * } - * } - * } - * ``` - * - * This is the simplest way to bridge the gap between your workflows and the UI, but using it - * requires your workflows code to reside in Android modules, instead of pure Kotlin. If this is a - * problem, or you need more flexibility for any other reason, you can use [ViewRegistry] to bind - * your renderings to [ComposeViewFactory] implementations at runtime. - */ -@WorkflowUiExperimentalApi -@Deprecated("Use ComposeScreen") -public interface ComposeRendering : AndroidViewRendering { - - /** Don't override this, override [Content] instead. */ - override val viewFactory: ViewFactory get() = Companion - - /** - * The composable content of this rendering. This method will be called with the current rendering - * instance as the receiver, any time a new rendering is emitted, or the [viewEnvironment] - * changes. - */ - @Composable public fun Content(viewEnvironment: ViewEnvironment) - - private companion object : ComposeViewFactory() { - /** - * Just returns [ComposeRendering]'s class, since this factory isn't for using with a view - * registry it doesn't matter. - */ - override val type: KClass = ComposeRendering::class - - @Composable override fun Content( - rendering: ComposeRendering, - viewEnvironment: ViewEnvironment - ) { - rendering.Content(viewEnvironment) - } - } -} - -/** - * Convenience function for creating anonymous [ComposeRendering]s since composable fun interfaces - * aren't supported. See the [ComposeRendering] class for more information. - */ -@WorkflowUiExperimentalApi -@Deprecated( - "Use ComposeScreen", - ReplaceWith("ComposeScreen(content)", "com.squareup.workflow1.ui.compose.ComposeScreen") -) -public inline fun ComposeRendering( - crossinline content: @Composable (ViewEnvironment) -> Unit -): ComposeRendering = object : ComposeRendering { - @Composable override fun Content(viewEnvironment: ViewEnvironment) { - content(viewEnvironment) - } -} diff --git a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/ComposeScreenViewFactory.kt b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/ComposeScreenViewFactory.kt index 905ceaa316..995afdd774 100644 --- a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/ComposeScreenViewFactory.kt +++ b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/ComposeScreenViewFactory.kt @@ -106,7 +106,7 @@ internal fun composeScreenViewFactory( * Often all the [composeScreenViewFactory] factories in an app need to share some context – * for example, certain composition locals need to be provided, such as `MaterialTheme`. * To configure this shared context, call [withCompositionRoot] on your top-level [ViewEnvironment]. - * The first time a [composeViewFactory] is used to show a rendering, its [Content] function will + * The first time a [composeScreenViewFactory] is used to show a rendering, its [Content] function will * be wrapped with the [CompositionRoot]. See the documentation on [CompositionRoot] for * more information. */ @@ -116,7 +116,7 @@ public abstract class ComposeScreenViewFactory : /** * The composable content of this [ScreenViewFactory]. This method will be called * any time [rendering] or [viewEnvironment] change. It is the Compose-based analogue of - * [ScreenViewRunner.showRendering][com.squareup.workflow1.ui.ScreenViewRunner.show]. + * [ScreenViewRunner.showRendering][com.squareup.workflow1.ui.ScreenViewRunner.showRendering]. */ @Composable public abstract fun Content( rendering: RenderingT, @@ -130,7 +130,7 @@ public abstract class ComposeScreenViewFactory : container: ViewGroup? ): ScreenViewHolder { val view = ComposeView(context) - return ScreenViewHolder(initialEnvironment, view) { rendering, environment -> + return ScreenViewHolder(initialEnvironment, view) { rendering, environment -> // Update the state whenever a new rendering is emitted. // This lambda will be executed synchronously before ScreenViewHolder.show returns. view.setContent { Content(rendering, environment) } diff --git a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/ComposeViewFactory.kt b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/ComposeViewFactory.kt deleted file mode 100644 index 57804e5b33..0000000000 --- a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/ComposeViewFactory.kt +++ /dev/null @@ -1,151 +0,0 @@ -// See https://youtrack.jetbrains.com/issue/KT-31734 -@file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry", "DEPRECATION") - -package com.squareup.workflow1.ui.compose - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.ComposeView -import com.squareup.workflow1.ui.LayoutRunner -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.ViewFactory -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.bindShowRendering -import kotlin.reflect.KClass - -/** - * Creates a [ViewFactory] that uses a [Composable] function to display the rendering. - * - * Simple usage: - * - * ``` - * val FooViewFactory = composeViewFactory { rendering, _ -> - * Text(rendering.message) - * } - * - * … - * - * val viewRegistry = ViewRegistry(FooViewFactory, …) - * ``` - * - * If you need to write a class instead of a function, for example to support dependency injection, - * see [ComposeViewFactory]. - * - * For more details about how to write composable view factories, see [ComposeViewFactory]. - */ -@WorkflowUiExperimentalApi -@Deprecated( - "Use composeScreenViewFactory", - ReplaceWith( - "composeScreenViewFactory(content)", - "com.squareup.workflow1.ui.compose.composeScreenViewFactory" - ) -) -public inline fun composeViewFactory( - noinline content: @Composable ( - rendering: RenderingT, - environment: ViewEnvironment - ) -> Unit -): ViewFactory = composeViewFactory(RenderingT::class, content) - -@PublishedApi -@WorkflowUiExperimentalApi -internal fun composeViewFactory( - type: KClass, - content: @Composable ( - rendering: RenderingT, - environment: ViewEnvironment - ) -> Unit -): ViewFactory = object : ComposeViewFactory() { - override val type: KClass = type - - @Composable override fun Content( - rendering: RenderingT, - viewEnvironment: ViewEnvironment - ) { - content(rendering, viewEnvironment) - } -} - -/** - * A [ViewFactory] that uses a [Composable] function to display the rendering. It is the - * Compose-based analogue of [LayoutRunner]. - * - * Simple usage: - * - * ``` - * class FooViewFactory : ComposeViewFactory() { - * override val type = Foo::class - * - * @Composable override fun Content( - * rendering: Foo, - * viewEnvironment: ViewEnvironment - * ) { - * Text(rendering.message) - * } - * } - * - * … - * - * val viewRegistry = ViewRegistry(FooViewFactory, …) - * ``` - * - * ## Nesting child renderings - * - * 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.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 - * for an example. - * - * Nested renderings will have access to any - * [composition locals][androidx.compose.runtime.CompositionLocal] defined in outer composable, even - * if there are legacy views in between them, as long as the [ViewEnvironment] is propagated - * continuously between the two factories. - * - * ## Initializing Compose context - * - * Often all the [composeViewFactory] factories in an app need to share some context – for example, - * certain composition locals need to be provided, such as `MaterialTheme`. To configure this shared - * context, call [withCompositionRoot] on your top-level [ViewEnvironment]. The first time a - * [composeViewFactory] is used to show a rendering, its [Content] function will be wrapped - * with the [CompositionRoot]. See the documentation on [CompositionRoot] for more information. - */ -@WorkflowUiExperimentalApi -@Deprecated("Use ComposeScreenViewFactory") -public abstract class ComposeViewFactory : ViewFactory { - - /** - * The composable content of this [ViewFactory]. This method will be called any time [rendering] - * or [viewEnvironment] change. It is the Compose-based analogue of [LayoutRunner.showRendering]. - */ - @Composable public abstract fun Content( - rendering: RenderingT, - viewEnvironment: ViewEnvironment - ) - - final override fun buildView( - initialRendering: RenderingT, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View = ComposeView(contextForNewView).also { composeView -> - // Update the state whenever a new rendering is emitted. - // This lambda will be executed synchronously before bindShowRendering returns. - composeView.bindShowRendering( - initialRendering, - initialViewEnvironment - ) { rendering, environment -> - // Entry point to the world of Compose. - composeView.setContent { - Content(rendering, environment) - } - } - } -} diff --git a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/CompositionRoot.kt b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/CompositionRoot.kt index 35e3f54baa..07f65424d1 100644 --- a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/CompositionRoot.kt +++ b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/CompositionRoot.kt @@ -1,4 +1,4 @@ -@file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry", "DEPRECATION") +@file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry") package com.squareup.workflow1.ui.compose @@ -11,10 +11,7 @@ import com.squareup.workflow1.ui.Screen import com.squareup.workflow1.ui.ScreenViewFactory import com.squareup.workflow1.ui.ScreenViewFactoryFinder 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 /** * Used by [WrappedWithRootIfNecessary] to ensure the [CompositionRoot] is only applied once. @@ -41,30 +38,9 @@ public typealias CompositionRoot = @Composable (content: @Composable () -> Unit) @WorkflowUiExperimentalApi public fun ViewEnvironment.withCompositionRoot(root: CompositionRoot): ViewEnvironment { return this + - (ScreenViewFactoryFinder to this[ScreenViewFactoryFinder].withCompositionRoot(root)) + - (ViewRegistry to this[ViewRegistry].withCompositionRoot(root)) + (ScreenViewFactoryFinder to this[ScreenViewFactoryFinder].withCompositionRoot(root)) } -/** - * Returns a [ViewRegistry] that ensures that any [composeViewFactory] factories registered in this - * registry will be wrapped exactly once with a [CompositionRoot] wrapper. - * See [CompositionRoot] for more information. - */ -@WorkflowUiExperimentalApi -@Deprecated("Use ScreenViewFactoryFinder.withCompositionRoot") -public fun ViewRegistry.withCompositionRoot(root: CompositionRoot): ViewRegistry = - mapFactories { factory -> - @Suppress("UNCHECKED_CAST") - (factory as? ComposeViewFactory)?.let { composeFactory -> - @Suppress("UNCHECKED_CAST") - composeViewFactory(composeFactory.type) { rendering, environment -> - WrappedWithRootIfNecessary(root) { - composeFactory.Content(rendering, environment) - } - } - } ?: factory - } - /** * Returns a [ScreenViewFactoryFinder] that ensures that any [composeScreenViewFactory] * factories registered in this registry will be wrapped exactly once with a [CompositionRoot] @@ -77,7 +53,6 @@ public fun ScreenViewFactoryFinder.withCompositionRoot( mapFactories { factory -> @Suppress("UNCHECKED_CAST") (factory as? ComposeScreenViewFactory)?.let { composeFactory -> - @Suppress("UNCHECKED_CAST") composeScreenViewFactory(composeFactory.type) { rendering, environment -> WrappedWithRootIfNecessary(root) { composeFactory.Content(rendering, environment) } } @@ -109,33 +84,6 @@ internal fun WrappedWithRootIfNecessary( } } -/** - * Applies [transform] to each [ViewFactory] in this registry. Transformations are applied lazily, - * at the time of lookup via [ViewRegistry.getEntryFor]. - */ -@WorkflowUiExperimentalApi -@Deprecated("Use ScreenViewFactoryFinder.mapFactories") -private fun ViewRegistry.mapFactories( - transform: (ViewFactory<*>) -> ViewFactory<*> -): ViewRegistry = object : ViewRegistry { - override val keys: Set> get() = this@mapFactories.keys - - override fun getEntryFor( - renderingType: KClass - ): ViewRegistry.Entry? { - val rawEntry = this@mapFactories.getEntryFor(renderingType) - val asViewFactory = (rawEntry as? ViewFactory<*>) ?: return rawEntry - - val transformedFactory = transform(asViewFactory) - check(transformedFactory.type == renderingType) { - "Expected transform to return a ViewFactory that is compatible with $renderingType, " + - "but got one with type ${transformedFactory.type}" - } - @Suppress("UNCHECKED_CAST") - return transformedFactory as ViewFactory - } -} - @WorkflowUiExperimentalApi private fun ScreenViewFactoryFinder.mapFactories( transform: (ScreenViewFactory<*>) -> ScreenViewFactory<*> diff --git a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/LegacyWorkflowRendering.kt b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/LegacyWorkflowRendering.kt deleted file mode 100644 index 3226a55294..0000000000 --- a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/LegacyWorkflowRendering.kt +++ /dev/null @@ -1,187 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.compose - -import android.view.View -import androidx.compose.foundation.layout.Box -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.key -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.viewinterop.AndroidView -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.Lifecycle.State.DESTROYED -import androidx.lifecycle.Lifecycle.State.INITIALIZED -import androidx.lifecycle.LifecycleEventObserver -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.LifecycleRegistry -import androidx.lifecycle.setViewTreeLifecycleOwner -import com.squareup.workflow1.ui.Compatible -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.WorkflowViewStub -import com.squareup.workflow1.ui.androidx.WorkflowLifecycleOwner -import com.squareup.workflow1.ui.getFactoryForRendering -import com.squareup.workflow1.ui.getShowRendering -import com.squareup.workflow1.ui.showRendering -import com.squareup.workflow1.ui.start -import kotlin.reflect.KClass - -@Deprecated("Use the overload with a `rendering: Screen` parameter") -@WorkflowUiExperimentalApi -@Composable -public fun WorkflowRendering( - rendering: Any, - viewEnvironment: ViewEnvironment, - modifier: Modifier = Modifier -) { - // This will fetch a new view factory any time the new rendering is incompatible with the previous - // one, as determined by Compatible. This corresponds to WorkflowViewStub's canShowRendering - // check. - val renderingCompatibilityKey = Compatible.keyFor(rendering) - - // By surrounding the below code with this key function, any time the new rendering is not - // compatible with the previous rendering we'll tear down the previous subtree of the composition, - // including its lifecycle, which destroys the lifecycle and any remembered state. If the view - // factory created an Android view, this will also remove the old one from the view hierarchy - // before replacing it with the new one. - key(renderingCompatibilityKey) { - val viewFactory = remember { - // The view registry may return a new factory instance for a rendering every time we ask it, for - // example if an AndroidViewRendering doesn't share its factory between rendering instances. We - // intentionally don't ask it for a new instance every time to match the behavior of - // WorkflowViewStub and other containers, which only ask for a new factory when the rendering is - // incompatible. - viewEnvironment[ViewRegistry] - // Can't use ViewRegistry.buildView here since we need the factory to convert it to a - // compose one. - .getFactoryForRendering(rendering) - .asComposeViewFactory() - } - - // Just like WorkflowViewStub, we need to manage a Lifecycle for the child view. We just provide - // a local here – ViewFactoryAndroidView will handle setting the appropriate view tree owners - // on the child view when necessary. Because this function is surrounded by a key() call, when - // the rendering is incompatible, the lifecycle for the old view will be destroyed. - val lifecycleOwner = rememberChildLifecycleOwner() - - CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) { - // We need to propagate min constraints because one of the likely uses for the modifier passed - // into this function is to directly control the layout of the child view – which means - // minimum constraints are likely to be significant. - Box(modifier, propagateMinConstraints = true) { - viewFactory.Content(rendering, viewEnvironment) - } - } - } -} - -/** - * Returns a [LifecycleOwner] that is a mirror of the current [LocalLifecycleOwner] until this - * function leaves the composition. Similar to [WorkflowLifecycleOwner] for views, but a - * bit simpler since we don't need to worry about attachment state. - */ -@Composable private fun rememberChildLifecycleOwner(): LifecycleOwner { - val lifecycleOwner = remember { - object : LifecycleOwner { - val registry = LifecycleRegistry(this) - override val lifecycle: Lifecycle - get() = registry - } - } - val parentLifecycle = LocalLifecycleOwner.current.lifecycle - - DisposableEffect(parentLifecycle) { - val parentObserver = LifecycleEventObserver { _, event -> - // Any time the parent lifecycle changes state, perform the same change on our lifecycle. - lifecycleOwner.registry.handleLifecycleEvent(event) - } - - parentLifecycle.addObserver(parentObserver) - onDispose { - parentLifecycle.removeObserver(parentObserver) - - // If we're leaving the composition it means the WorkflowRendering is either going away itself - // or about to switch to an incompatible rendering – either way, this lifecycle is dead. Note - // that we can't transition from INITIALIZED to DESTROYED – the LifecycleRegistry will throw. - // WorkflowLifecycleOwner has this same check. - if (lifecycleOwner.registry.currentState != INITIALIZED) { - lifecycleOwner.registry.currentState = DESTROYED - } - } - } - - return lifecycleOwner -} - -/** - * Returns a [ComposeViewFactory] that makes it convenient to display this [ViewFactory] as a - * composable. If this is a [ComposeViewFactory] already it just returns `this`, otherwise it wraps - * the factory in one that manages a classic Android view. - */ -@OptIn(WorkflowUiExperimentalApi::class) -private fun ViewFactory.asComposeViewFactory() = - (this as? ComposeViewFactory) ?: object : ComposeViewFactory() { - - private val originalFactory = this@asComposeViewFactory - override val type: KClass get() = originalFactory.type - - /** - * This is effectively the logic of [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 - * of replacing itself in its parent doesn't play nicely with Compose. - * - Allows us to pass the correct parent view for inflation (the root of the composition). - * - Avoids `WorkflowViewStub` having to do its own lookup to find the correct [ViewFactory], since - * we already have the correct one. - * - Propagate the current [LifecycleOwner] from [LocalLifecycleOwner] by setting it as the - * [ViewTreeLifecycleOwner] on the view. - * - * Like `WorkflowViewStub`, this function uses the [originalFactory] to create and memoize a - * [View] to display the [rendering], keeps it updated with the latest [rendering] and - * [viewEnvironment], and adds it to the composition. - */ - @Composable override fun Content( - rendering: R, - viewEnvironment: ViewEnvironment - ) { - val lifecycleOwner = LocalLifecycleOwner.current - - AndroidView( - factory = { context -> - // We pass in a null container because the container isn't a View, it's a composable. The - // compose machinery will generate an intermediate view that it ends up adding this to but - // we don't have access to that. - originalFactory.buildView(rendering, viewEnvironment, context, container = null) - .also { view -> - view.start() - - // Mirrors the check done in ViewRegistry.buildView. - checkNotNull(view.getShowRendering()) { - "View.bindShowRendering should have been called for $view, typically by the " + - "${ViewFactory::class.java.name} that created it." - } - - // Unfortunately AndroidView doesn't propagate this itself. - view.setViewTreeLifecycleOwner(lifecycleOwner) - // We don't propagate the (non-compose) SavedStateRegistryOwner, or the (compose) - // SaveableStateRegistry, because currently all our navigation is implemented as - // Android views, which ensures there is always an Android view between any state - // registry and any Android view shown as a child of it, even if there's a compose - // view in between. - } - }, - // This function will be invoked every time this composable is recomposed, which means that - // any time a new rendering or view environment are passed in we'll send them to the view. - update = { view -> - view.showRendering(rendering, viewEnvironment) - } - ) - } - } diff --git a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/TextControllerAsMutableState.kt b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/TextControllerAsMutableState.kt index 6b20df6e83..f78073649a 100644 --- a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/TextControllerAsMutableState.kt +++ b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/TextControllerAsMutableState.kt @@ -10,7 +10,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.snapshotFlow import com.squareup.workflow1.ui.TextController import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch /** diff --git a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/WorkflowRendering.kt b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/WorkflowRendering.kt index 9164e0de6e..09e85ae9ab 100644 --- a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/WorkflowRendering.kt +++ b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/WorkflowRendering.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") - package com.squareup.workflow1.ui.compose import android.view.View @@ -28,8 +26,6 @@ import com.squareup.workflow1.ui.ScreenViewFactory import com.squareup.workflow1.ui.ScreenViewFactoryFinder import com.squareup.workflow1.ui.ScreenViewHolder 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.WorkflowViewStub import com.squareup.workflow1.ui.androidx.OnBackPressedDispatcherOwnerKey @@ -64,8 +60,7 @@ import kotlin.reflect.KClass * } * ``` * - * @param rendering The workflow rendering to display. May be of any type for which a [ViewFactory] - * has been registered in [viewEnvironment]'s [ViewRegistry]. + * @param rendering The workflow rendering to display. * @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. @@ -171,8 +166,8 @@ private fun ScreenViewFactory.asComposeViewFactory() * - Avoids extra custom views required to host `WorkflowViewStub` inside a Composition. Its trick * of replacing itself in its parent doesn't play nicely with Compose. * - Allows us to pass the correct parent view for inflation (the root of the composition). - * - Avoids `WorkflowViewStub` having to do its own lookup to find the correct [ViewFactory], since - * we already have the correct one. + * - Avoids `WorkflowViewStub` having to do its own lookup to find the correct + * [ScreenViewFactory], since we already have the correct one. * - Propagate the current [LifecycleOwner] from [LocalLifecycleOwner] by setting it as the * [ViewTreeLifecycleOwner] on the view. * - Propagate the current [OnBackPressedDispatcherOwner] from either diff --git a/workflow-ui/container-android/api/container-android.api b/workflow-ui/container-android/api/container-android.api deleted file mode 100644 index fb56d04319..0000000000 --- a/workflow-ui/container-android/api/container-android.api +++ /dev/null @@ -1,80 +0,0 @@ -public final class com/squareup/workflow1/ui/backstack/BackStackContainer { - public static final field Companion Lcom/squareup/workflow1/ui/backstack/BackStackContainer$Companion; - public fun ()V -} - -public final class com/squareup/workflow1/ui/backstack/BackStackContainer$Companion : com/squareup/workflow1/ui/ViewFactory { - public fun buildView (Lcom/squareup/workflow1/ui/backstack/BackStackScreen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; - public synthetic 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 final class com/squareup/workflow1/ui/modal/AlertContainer : com/squareup/workflow1/ui/modal/ModalContainer { - public static final field Companion Lcom/squareup/workflow1/ui/modal/AlertContainer$Companion; - public fun (Landroid/content/Context;)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;I)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;II)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;III)V - public synthetic fun (Landroid/content/Context;Landroid/util/AttributeSet;IIIILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun buildDialog (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/modal/ModalContainer$DialogRef; -} - -public final class com/squareup/workflow1/ui/modal/AlertContainer$Companion : com/squareup/workflow1/ui/ViewFactory { - public fun buildView (Lcom/squareup/workflow1/ui/modal/AlertContainerScreen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; - public synthetic fun buildView (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; - public final fun customThemeBinding (I)Lcom/squareup/workflow1/ui/ViewFactory; - public static synthetic fun customThemeBinding$default (Lcom/squareup/workflow1/ui/modal/AlertContainer$Companion;IILjava/lang/Object;)Lcom/squareup/workflow1/ui/ViewFactory; - public fun getType ()Lkotlin/reflect/KClass; -} - -public abstract class com/squareup/workflow1/ui/modal/ModalContainer : android/widget/FrameLayout { - public fun (Landroid/content/Context;)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;I)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;II)V - public synthetic fun (Landroid/content/Context;Landroid/util/AttributeSet;IIILkotlin/jvm/internal/DefaultConstructorMarker;)V - protected abstract fun buildDialog (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/modal/ModalContainer$DialogRef; - protected fun onAttachedToWindow ()V - protected fun onDetachedFromWindow ()V - protected fun onRestoreInstanceState (Landroid/os/Parcelable;)V - protected fun onSaveInstanceState ()Landroid/os/Parcelable; - protected final fun update (Lcom/squareup/workflow1/ui/modal/HasModals;Lcom/squareup/workflow1/ui/ViewEnvironment;)V - protected abstract fun updateDialog (Lcom/squareup/workflow1/ui/modal/ModalContainer$DialogRef;)V -} - -protected final class com/squareup/workflow1/ui/modal/ModalContainer$DialogRef { - public fun (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Ljava/lang/Object;)V - public synthetic fun (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Ljava/lang/Object;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun copy (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Ljava/lang/Object;)Lcom/squareup/workflow1/ui/modal/ModalContainer$DialogRef; - public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/modal/ModalContainer$DialogRef;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Ljava/lang/Object;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/modal/ModalContainer$DialogRef; - public fun equals (Ljava/lang/Object;)Z - public final fun getDialog ()Landroid/app/Dialog; - public final fun getExtra ()Ljava/lang/Object; - public final fun getModalRendering ()Ljava/lang/Object; - public final fun getViewEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment; - public fun hashCode ()I -} - -public class com/squareup/workflow1/ui/modal/ModalViewContainer : com/squareup/workflow1/ui/modal/ModalContainer { - public static final field Companion Lcom/squareup/workflow1/ui/modal/ModalViewContainer$Companion; - public fun (Landroid/content/Context;)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;I)V - public fun (Landroid/content/Context;Landroid/util/AttributeSet;II)V - public synthetic fun (Landroid/content/Context;Landroid/util/AttributeSet;IIILkotlin/jvm/internal/DefaultConstructorMarker;)V - protected final fun buildDialog (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/modal/ModalContainer$DialogRef; - public fun buildDialogForView (Landroid/view/View;)Landroid/app/Dialog; - protected fun updateDialog (Lcom/squareup/workflow1/ui/modal/ModalContainer$DialogRef;)V -} - -public final class com/squareup/workflow1/ui/modal/ModalViewContainer$Companion { -} - -public final class com/squareup/workflow1/ui/modal/ModalViewContainer$ModalViewFactory : com/squareup/workflow1/ui/ViewFactory { - public fun (ILkotlin/reflect/KClass;)V - public fun buildView (Lcom/squareup/workflow1/ui/modal/HasModals;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; - public synthetic 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; -} - diff --git a/workflow-ui/container-android/build.gradle.kts b/workflow-ui/container-android/build.gradle.kts deleted file mode 100644 index d385efcb5f..0000000000 --- a/workflow-ui/container-android/build.gradle.kts +++ /dev/null @@ -1,34 +0,0 @@ -plugins { - id("com.android.library") - id("kotlin-android") - id("android-defaults") - id("android-ui-tests") - id("published") -} - -android { - namespace = "com.squareup.workflow1.ui.container" -} - -dependencies { - androidTestImplementation(libs.truth) - - api(libs.kotlin.jdk6) - - api(project(":workflow-ui:container-common")) - api(project(":workflow-ui:core-common")) - - implementation(libs.androidx.appcompat) - implementation(libs.androidx.lifecycle.common) - - implementation(project(":workflow-ui:core-android")) - - testImplementation(libs.androidx.test.core) - testImplementation(libs.junit) - testImplementation(libs.kotlin.test.jdk) - testImplementation(libs.kotlinx.coroutines.test) - testImplementation(libs.mockito.kotlin) - testImplementation(libs.robolectric) - testImplementation(libs.robolectric.annotations) - testImplementation(libs.truth) -} diff --git a/workflow-ui/container-android/dependencies/releaseRuntimeClasspath.txt b/workflow-ui/container-android/dependencies/releaseRuntimeClasspath.txt deleted file mode 100644 index ab2805755a..0000000000 --- a/workflow-ui/container-android/dependencies/releaseRuntimeClasspath.txt +++ /dev/null @@ -1,51 +0,0 @@ -androidx.activity:activity:1.6.1 -androidx.annotation:annotation-experimental:1.3.0 -androidx.annotation:annotation-jvm:1.6.0 -androidx.annotation:annotation:1.6.0 -androidx.appcompat:appcompat-resources:1.6.1 -androidx.appcompat:appcompat:1.6.1 -androidx.arch.core:core-common:2.2.0 -androidx.arch.core:core-runtime:2.2.0 -androidx.collection:collection:1.1.0 -androidx.concurrent:concurrent-futures:1.1.0 -androidx.core:core-ktx:1.10.0 -androidx.core:core:1.10.0 -androidx.cursoradapter:cursoradapter:1.0.0 -androidx.customview:customview:1.0.0 -androidx.drawerlayout:drawerlayout:1.0.0 -androidx.emoji2:emoji2-views-helper:1.2.0 -androidx.emoji2:emoji2:1.2.0 -androidx.fragment:fragment:1.3.6 -androidx.interpolator:interpolator:1.0.0 -androidx.lifecycle:lifecycle-common:2.6.1 -androidx.lifecycle:lifecycle-livedata-core:2.6.1 -androidx.lifecycle:lifecycle-livedata:2.6.1 -androidx.lifecycle:lifecycle-process:2.6.1 -androidx.lifecycle:lifecycle-runtime-ktx:2.6.1 -androidx.lifecycle:lifecycle-runtime:2.6.1 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1 -androidx.lifecycle:lifecycle-viewmodel:2.6.1 -androidx.loader:loader:1.0.0 -androidx.profileinstaller:profileinstaller:1.3.0 -androidx.resourceinspection:resourceinspection-annotation:1.0.1 -androidx.savedstate:savedstate:1.2.1 -androidx.startup:startup-runtime:1.1.1 -androidx.tracing:tracing:1.0.0 -androidx.transition:transition:1.4.1 -androidx.vectordrawable:vectordrawable-animated:1.1.0 -androidx.vectordrawable:vectordrawable:1.1.0 -androidx.versionedparcelable:versionedparcelable:1.1.1 -androidx.viewpager:viewpager:1.0.0 -com.google.guava:listenablefuture:1.0 -com.squareup.okio:okio-jvm:3.3.0 -com.squareup.okio:okio:3.3.0 -org.jetbrains.kotlin:kotlin-bom:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib-common:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib:1.9.10 -org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -org.jetbrains:annotations:23.0.0 diff --git a/workflow-ui/container-android/gradle.properties b/workflow-ui/container-android/gradle.properties deleted file mode 100644 index 3b366b69a4..0000000000 --- a/workflow-ui/container-android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -POM_ARTIFACT_ID=workflow-ui-container-android -POM_NAME=Workflow UI Container Android -POM_PACKAGING=aar diff --git a/workflow-ui/container-android/src/androidTest/AndroidManifest.xml b/workflow-ui/container-android/src/androidTest/AndroidManifest.xml deleted file mode 100644 index 65f648973b..0000000000 --- a/workflow-ui/container-android/src/androidTest/AndroidManifest.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/modal/test/ModalViewContainerLifecycleActivity.kt b/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/modal/test/ModalViewContainerLifecycleActivity.kt deleted file mode 100644 index 6e798699a9..0000000000 --- a/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/modal/test/ModalViewContainerLifecycleActivity.kt +++ /dev/null @@ -1,64 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.modal.test - -import android.view.View -import android.widget.FrameLayout -import com.squareup.workflow1.ui.Compatible -import com.squareup.workflow1.ui.Screen -import com.squareup.workflow1.ui.ScreenViewFactory -import com.squareup.workflow1.ui.ScreenViewFactory.Companion.fromCode -import com.squareup.workflow1.ui.ScreenViewHolder -import com.squareup.workflow1.ui.ViewRegistry -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.WorkflowViewStub -import com.squareup.workflow1.ui.asScreen -import com.squareup.workflow1.ui.internal.test.AbstractLifecycleTestActivity -import com.squareup.workflow1.ui.modal.HasModals -import com.squareup.workflow1.ui.modal.ModalViewContainer -import com.squareup.workflow1.ui.modal.test.ModalViewContainerLifecycleActivity.TestRendering.LeafRendering -import com.squareup.workflow1.ui.modal.test.ModalViewContainerLifecycleActivity.TestRendering.RecurseRendering - -@OptIn(WorkflowUiExperimentalApi::class) -internal class ModalViewContainerLifecycleActivity : AbstractLifecycleTestActivity() { - - object BaseRendering : - Screen, - ScreenViewFactory by ScreenViewFactory.fromCode( - buildView = { _, environment, context, _ -> - ScreenViewHolder(environment, View(context)) { _, _ -> } - } - ) - - data class TestModals( - override val modals: List - ) : HasModals { - override val beneathModals: BaseRendering get() = BaseRendering - } - - sealed class TestRendering : Screen { - data class LeafRendering(val name: String) : TestRendering(), Compatible { - override val compatibilityKey: String get() = name - } - - data class RecurseRendering(val wrapped: LeafRendering) : TestRendering() - } - - override val viewRegistry: ViewRegistry = ViewRegistry( - ModalViewContainer.binding(), - BaseRendering, - leafViewBinding(LeafRendering::class, lifecycleLoggingViewObserver { it.name }), - fromCode { _, environment, context, _ -> - val stub = WorkflowViewStub(context) - val frame = FrameLayout(context).also { container -> - container.addView(stub) - } - ScreenViewHolder(environment, frame) { rendering, viewEnvironment -> - stub.show(asScreen(TestModals(listOf(rendering.wrapped))), viewEnvironment) - } - } - ) - - fun update(vararg modals: TestRendering) = - setRendering(asScreen(TestModals(modals.asList()))) -} diff --git a/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/modal/test/ModalViewContainerLifecycleTest.kt b/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/modal/test/ModalViewContainerLifecycleTest.kt deleted file mode 100644 index 6b871edeb2..0000000000 --- a/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/modal/test/ModalViewContainerLifecycleTest.kt +++ /dev/null @@ -1,402 +0,0 @@ -@file:OptIn(WorkflowUiExperimentalApi::class) - -package com.squareup.workflow1.ui.modal.test - -import androidx.lifecycle.Lifecycle.State.CREATED -import androidx.lifecycle.Lifecycle.State.RESUMED -import androidx.lifecycle.LifecycleOwner -import androidx.test.ext.junit.rules.ActivityScenarioRule -import com.google.common.truth.Truth.assertThat -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule -import com.squareup.workflow1.ui.modal.ModalViewContainer -import com.squareup.workflow1.ui.modal.test.ModalViewContainerLifecycleActivity.TestRendering.LeafRendering -import com.squareup.workflow1.ui.modal.test.ModalViewContainerLifecycleActivity.TestRendering.RecurseRendering -import leakcanary.DetectLeaksAfterTestSuccess -import org.junit.Rule -import org.junit.Test -import org.junit.rules.RuleChain - -/** - * Tests for [ModalViewContainer]'s [LifecycleOwner] integration. - */ -internal class ModalViewContainerLifecycleTest { - - private val scenarioRule = - ActivityScenarioRule(ModalViewContainerLifecycleActivity::class.java) - - @get:Rule val rules = RuleChain.outerRule(DetectLeaksAfterTestSuccess()) - .around(scenarioRule) - .around(IdlingDispatcherRule) - private val scenario get() = scenarioRule.scenario - - /** - * We test stop instead of pause because on older Android versions (e.g. level 21), - * `moveToState(STARTED)` will also stop the lifecycle, not just pause it. By just using stopped, - * which is consistent across all the versions we care about, we don't need to special-case our - * assertions, but we're still testing fundamentally the same thing (moving between non-terminal - * lifecycle states). - */ - @Test fun stop_then_resume() { - assertThat(scenario.state).isEqualTo(RESUMED) - scenario.onActivity { - it.update(LeafRendering("initial")) - } - - scenario.onActivity { activity -> - assertThat(activity.consumeLifecycleEvents()).containsExactly( - "activity onCreate", - "activity onStart", - "activity onResume", - "LeafView initial onAttached", - "LeafView initial ON_CREATE", - "LeafView initial ON_START", - "LeafView initial ON_RESUME", - ) - } - - scenario.moveToState(CREATED) - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "LeafView initial ON_PAUSE", - "activity onPause", - "LeafView initial ON_STOP", - "activity onStop", - ) - } - - scenario.moveToState(RESUMED) - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "activity onStart", - "LeafView initial ON_START", - "activity onResume", - "LeafView initial ON_RESUME", - ) - } - } - - @Test fun recreate_rendering() { - assertThat(scenario.state).isEqualTo(RESUMED) - scenario.onActivity { - it.update(LeafRendering("initial")) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "activity onCreate", - "activity onStart", - "activity onResume", - "LeafView initial onAttached", - "LeafView initial ON_CREATE", - "LeafView initial ON_START", - "LeafView initial ON_RESUME", - ) - } - - scenario.onActivity { - it.recreateViewsOnNextRendering() - it.update(LeafRendering("recreated")) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "LeafView initial ON_PAUSE", - "LeafView initial ON_STOP", - "LeafView initial ON_DESTROY", - "LeafView initial onDetached", - "LeafView recreated onAttached", - "LeafView recreated ON_CREATE", - "LeafView recreated ON_START", - "LeafView recreated ON_RESUME", - ) - } - } - - @Test fun recreate_activity() { - lateinit var initialActivity: ModalViewContainerLifecycleActivity - - assertThat(scenario.state).isEqualTo(RESUMED) - scenario.onActivity { - it.update(LeafRendering("initial")) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "activity onCreate", - "activity onStart", - "activity onResume", - "LeafView initial onAttached", - "LeafView initial ON_CREATE", - "LeafView initial ON_START", - "LeafView initial ON_RESUME", - ) - - // Store a reference to the activity so we can get events from it after destroying. - initialActivity = it - it.restoreRenderingAfterConfigChange = false - } - - scenario.recreate() - scenario.onActivity { - assertThat(it).isNotSameInstanceAs(initialActivity) - it.update(LeafRendering("recreated")) - } - - scenario.onActivity { - assertThat(initialActivity.consumeLifecycleEvents()).containsExactly( - "LeafView initial ON_PAUSE", - "activity onPause", - "LeafView initial ON_STOP", - "activity onStop", - "LeafView initial onDetached", - "LeafView initial ON_DESTROY", - "activity onDestroy", - ) - - assertThat(it.consumeLifecycleEvents()).containsExactly( - "activity onCreate", - "activity onStart", - "activity onResume", - "LeafView recreated onAttached", - "LeafView recreated ON_CREATE", - "LeafView recreated ON_START", - "LeafView recreated ON_RESUME", - ) - } - } - - @Test fun replace_modal() { - assertThat(scenario.state).isEqualTo(RESUMED) - scenario.onActivity { - it.update(LeafRendering("initial")) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "activity onCreate", - "activity onStart", - "activity onResume", - "LeafView initial onAttached", - "LeafView initial ON_CREATE", - "LeafView initial ON_START", - "LeafView initial ON_RESUME", - ) - } - - scenario.onActivity { - it.update(LeafRendering("next")) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "LeafView initial onDetached", - "LeafView initial ON_PAUSE", - "LeafView initial ON_STOP", - "LeafView initial ON_DESTROY", - "LeafView next onAttached", - "LeafView next ON_CREATE", - "LeafView next ON_START", - "LeafView next ON_RESUME", - ) - } - } - - @Test fun replace_after_stop() { - assertThat(scenario.state).isEqualTo(RESUMED) - scenario.onActivity { - it.update(LeafRendering("initial")) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "activity onCreate", - "activity onStart", - "activity onResume", - "LeafView initial onAttached", - "LeafView initial ON_CREATE", - "LeafView initial ON_START", - "LeafView initial ON_RESUME", - ) - } - - scenario.moveToState(CREATED) - - scenario.onActivity { - it.update(LeafRendering("next")) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "LeafView initial ON_PAUSE", - "activity onPause", - "LeafView initial ON_STOP", - "activity onStop", - "LeafView initial onDetached", - "LeafView initial ON_DESTROY", - "LeafView next onAttached", - "LeafView next ON_CREATE", - ) - } - } - - @Test fun nested_lifecycle() { - assertThat(scenario.state).isEqualTo(RESUMED) - scenario.onActivity { - it.consumeLifecycleEvents() - it.update(RecurseRendering(LeafRendering("wrapped"))) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "LeafView wrapped onAttached", - "LeafView wrapped ON_CREATE", - "LeafView wrapped ON_START", - "LeafView wrapped ON_RESUME", - ) - } - - scenario.onActivity { - it.update(LeafRendering("unwrapped")) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "LeafView wrapped ON_PAUSE", - "LeafView wrapped ON_STOP", - "LeafView wrapped ON_DESTROY", - "LeafView wrapped onDetached", - "LeafView unwrapped onAttached", - "LeafView unwrapped ON_CREATE", - "LeafView unwrapped ON_START", - "LeafView unwrapped ON_RESUME", - ) - } - } - - @Test - fun separate_modal_lifecycles_are_independent() { - assertThat(scenario.state).isEqualTo(RESUMED) - scenario.onActivity { - it.update( - LeafRendering("1 initial"), - LeafRendering("2 initial"), - ) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "activity onCreate", - "activity onStart", - "activity onResume", - "LeafView 1 initial onAttached", - "LeafView 1 initial ON_CREATE", - "LeafView 1 initial ON_START", - "LeafView 1 initial ON_RESUME", - "LeafView 2 initial onAttached", - "LeafView 2 initial ON_CREATE", - "LeafView 2 initial ON_START", - "LeafView 2 initial ON_RESUME", - ) - } - - // Change the rendering on only one of the modals. - scenario.onActivity { - it.update( - LeafRendering("1 initial"), - LeafRendering("2 next"), - ) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "LeafView 2 initial onDetached", - "LeafView 2 initial ON_PAUSE", - "LeafView 2 initial ON_STOP", - "LeafView 2 initial ON_DESTROY", - "LeafView 2 next onAttached", - "LeafView 2 next ON_CREATE", - "LeafView 2 next ON_START", - "LeafView 2 next ON_RESUME", - ) - } - - // Change the rendering on the other modal. - scenario.onActivity { - it.update( - LeafRendering("1 next"), - LeafRendering("2 next"), - ) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "LeafView 1 initial onDetached", - "LeafView 1 initial ON_PAUSE", - "LeafView 1 initial ON_STOP", - "LeafView 1 initial ON_DESTROY", - "LeafView 1 next onAttached", - "LeafView 1 next ON_CREATE", - "LeafView 1 next ON_START", - "LeafView 1 next ON_RESUME", - ) - } - } - - @Test fun all_modals_share_parent_lifecycle() { - assertThat(scenario.state).isEqualTo(RESUMED) - scenario.onActivity { - it.update( - LeafRendering("1 initial"), - LeafRendering("2 initial"), - ) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "activity onCreate", - "activity onStart", - "activity onResume", - "LeafView 1 initial onAttached", - "LeafView 1 initial ON_CREATE", - "LeafView 1 initial ON_START", - "LeafView 1 initial ON_RESUME", - "LeafView 2 initial onAttached", - "LeafView 2 initial ON_CREATE", - "LeafView 2 initial ON_START", - "LeafView 2 initial ON_RESUME", - ) - } - - scenario.onActivity { - it.recreateViewsOnNextRendering() - it.update( - LeafRendering("1 recreated"), - LeafRendering("2 recreated"), - ) - } - - scenario.onActivity { - assertThat(it.consumeLifecycleEvents()).containsExactly( - "LeafView 2 initial ON_PAUSE", - "LeafView 2 initial ON_STOP", - "LeafView 2 initial ON_DESTROY", - "LeafView 2 initial onDetached", - "LeafView 1 initial ON_PAUSE", - "LeafView 1 initial ON_STOP", - "LeafView 1 initial ON_DESTROY", - "LeafView 1 initial onDetached", - "LeafView 1 recreated onAttached", - "LeafView 1 recreated ON_CREATE", - "LeafView 1 recreated ON_START", - "LeafView 1 recreated ON_RESUME", - "LeafView 2 recreated onAttached", - "LeafView 2 recreated ON_CREATE", - "LeafView 2 recreated ON_START", - "LeafView 2 recreated ON_RESUME", - ) - } - } -} diff --git a/workflow-ui/container-android/src/main/baseline-profile.txt b/workflow-ui/container-android/src/main/baseline-profile.txt deleted file mode 100644 index e467485a55..0000000000 --- a/workflow-ui/container-android/src/main/baseline-profile.txt +++ /dev/null @@ -1,32 +0,0 @@ -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory$1$1$1;->(Ljava/lang/Object;)V -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory$1$1$1;->invoke(Lcom/squareup/workflow1/ui/modal/HasModals;Lcom/squareup/workflow1/ui/ViewEnvironment;)V -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory$1$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory$1;->(I)V -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory$1;->invoke(Lcom/squareup/workflow1/ui/modal/AlertContainerScreen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory;->(I)V -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory;->(IILkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory;->buildView(Lcom/squareup/workflow1/ui/modal/AlertContainerScreen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory;->getType()Lkotlin/reflect/KClass; -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$Companion;->()V -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$Companion;->buildView(Lcom/squareup/workflow1/ui/modal/AlertContainerScreen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$Companion;->buildView(Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; -HSPLcom/squareup/workflow1/ui/modal/AlertContainer$Companion;->getType()Lkotlin/reflect/KClass; -HSPLcom/squareup/workflow1/ui/modal/AlertContainer;->(Landroid/content/Context;Landroid/util/AttributeSet;III)V -HSPLcom/squareup/workflow1/ui/modal/AlertContainer;->(Landroid/content/Context;Landroid/util/AttributeSet;IIIILkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLcom/squareup/workflow1/ui/modal/ModalContainer$parentLifecycleOwner$2;->(Lcom/squareup/workflow1/ui/modal/ModalContainer;)V -HSPLcom/squareup/workflow1/ui/modal/ModalContainer;->(Landroid/content/Context;Landroid/util/AttributeSet;II)V -HSPLcom/squareup/workflow1/ui/modal/ModalContainer;->onAttachedToWindow()V -HSPLcom/squareup/workflow1/ui/modal/ModalContainer;->update(Lcom/squareup/workflow1/ui/modal/HasModals;Lcom/squareup/workflow1/ui/ViewEnvironment;)V -Lcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory$1$1$1; -Lcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory$1; -Lcom/squareup/workflow1/ui/modal/AlertContainer$AlertContainerViewFactory; -Lcom/squareup/workflow1/ui/modal/AlertContainer$Companion; -Lcom/squareup/workflow1/ui/modal/AlertContainer; -Lcom/squareup/workflow1/ui/modal/ModalContainer$DialogRef; -Lcom/squareup/workflow1/ui/modal/ModalContainer$parentLifecycleOwner$2; -Lcom/squareup/workflow1/ui/modal/ModalContainer$update$2$1$1; -Lcom/squareup/workflow1/ui/modal/ModalContainer$update$2$1$2; -Lcom/squareup/workflow1/ui/modal/ModalContainer; -PLcom/squareup/workflow1/ui/modal/ModalContainer;->onDetachedFromWindow()V diff --git a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/backstack/BackStackContainer.kt b/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/backstack/BackStackContainer.kt deleted file mode 100644 index 35ee23476c..0000000000 --- a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/backstack/BackStackContainer.kt +++ /dev/null @@ -1,21 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.backstack - -import com.squareup.workflow1.ui.DecorativeViewFactory -import com.squareup.workflow1.ui.ViewFactory -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi - -/** - * BackStackContainer has been promoted to the core workflow-ui modules, - * and is now built into [ViewRegistry][com.squareup.workflow1.ui.ViewRegistry] by default. - * - * This stub has been left in place to preserve the name of the legacy [ViewFactory], - * to ease conversion. - */ -@Deprecated("Use com.squareup.workflow1.ui.container.BackStackContainer") -@WorkflowUiExperimentalApi -public class BackStackContainer { - public companion object : ViewFactory> - by DecorativeViewFactory(BackStackScreen::class, { legacy -> legacy.asNonLegacy() }) -} diff --git a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/AlertContainer.kt b/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/AlertContainer.kt deleted file mode 100644 index c3968d3801..0000000000 --- a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/AlertContainer.kt +++ /dev/null @@ -1,110 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.modal - -import android.content.Context -import android.content.DialogInterface -import android.util.AttributeSet -import android.view.View -import android.view.ViewGroup -import android.view.ViewGroup.LayoutParams.MATCH_PARENT -import androidx.annotation.StyleRes -import androidx.appcompat.app.AlertDialog -import com.squareup.workflow1.ui.BuilderViewFactory -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.ViewFactory -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.bindShowRendering -import com.squareup.workflow1.ui.container.R -import com.squareup.workflow1.ui.modal.AlertScreen.Button -import com.squareup.workflow1.ui.modal.AlertScreen.Button.NEGATIVE -import com.squareup.workflow1.ui.modal.AlertScreen.Button.NEUTRAL -import com.squareup.workflow1.ui.modal.AlertScreen.Button.POSITIVE -import com.squareup.workflow1.ui.modal.AlertScreen.Event.ButtonClicked -import com.squareup.workflow1.ui.modal.AlertScreen.Event.Canceled - -/** - * Renders the [AlertScreen]s of an [AlertContainerScreen] as [AlertDialog]s. - */ -@WorkflowUiExperimentalApi -@Deprecated("Use BodyAndModalsContainer and the built in OverlayDialogFactory for AlertOverlay") -public class AlertContainer @JvmOverloads constructor( - context: Context, - attributeSet: AttributeSet? = null, - defStyle: Int = 0, - defStyleRes: Int = 0, - @StyleRes private val dialogThemeResId: Int = 0 -) : ModalContainer(context, attributeSet, defStyle, defStyleRes) { - - override fun buildDialog( - initialModalRendering: AlertScreen, - initialViewEnvironment: ViewEnvironment - ): DialogRef { - val dialog = AlertDialog.Builder(context, dialogThemeResId) - .create() - val ref = DialogRef(initialModalRendering, initialViewEnvironment, dialog) - updateDialog(ref) - return ref - } - - override fun updateDialog(dialogRef: DialogRef) { - val dialog = dialogRef.dialog as AlertDialog - val rendering = dialogRef.modalRendering - - if (rendering.cancelable) { - dialog.setOnCancelListener { rendering.onEvent(Canceled) } - dialog.setCancelable(true) - } else { - dialog.setCancelable(false) - } - - for (button in Button.values()) { - rendering.buttons[button] - ?.let { name -> - dialog.setButton(button.toId(), name) { _, _ -> - rendering.onEvent(ButtonClicked(button)) - } - } - ?: run { - dialog.getButton(button.toId()) - ?.visibility = View.INVISIBLE - } - } - - dialog.setMessage(rendering.message) - dialog.setTitle(rendering.title) - } - - private fun Button.toId(): Int = when (this) { - POSITIVE -> DialogInterface.BUTTON_POSITIVE - NEGATIVE -> DialogInterface.BUTTON_NEGATIVE - NEUTRAL -> DialogInterface.BUTTON_NEUTRAL - } - - private class AlertContainerViewFactory( - @StyleRes private val dialogThemeResId: Int = 0 - ) : ViewFactory> by BuilderViewFactory( - type = AlertContainerScreen::class, - viewConstructor = { initialRendering, initialEnv, context, _ -> - AlertContainer(context, dialogThemeResId = dialogThemeResId) - .apply { - id = R.id.workflow_alert_container - layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT) - bindShowRendering(initialRendering, initialEnv, ::update) - } - } - ) - - public companion object : ViewFactory> by AlertContainerViewFactory() { - /** - * Creates a [ViewFactory] to show the [AlertScreen]s of an [AlertContainerScreen] - * as Android `AlertDialog`s. - * - * @param dialogThemeResId the resource ID of the theme against which to inflate - * dialogs. Defaults to `0` to use the parent `context`'s default alert dialog theme. - */ - public fun customThemeBinding( - @StyleRes dialogThemeResId: Int = 0 - ): ViewFactory> = AlertContainerViewFactory(dialogThemeResId) - } -} diff --git a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/ModalContainer.kt b/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/ModalContainer.kt deleted file mode 100644 index f4a8ad88c8..0000000000 --- a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/ModalContainer.kt +++ /dev/null @@ -1,309 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.modal - -import android.app.Dialog -import android.content.Context -import android.os.Bundle -import android.os.Parcel -import android.os.Parcelable -import android.os.Parcelable.Creator -import android.util.AttributeSet -import android.view.View -import android.view.ViewGroup -import android.view.ViewGroup.LayoutParams.MATCH_PARENT -import android.widget.FrameLayout -import androidx.activity.OnBackPressedDispatcherOwner -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import com.squareup.workflow1.ui.Compatible -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.WorkflowViewStub -import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.onBackPressedDispatcherOwner -import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.stateRegistryOwnerFromViewTreeOrContext -import com.squareup.workflow1.ui.androidx.WorkflowLifecycleOwner -import com.squareup.workflow1.ui.androidx.WorkflowSavedStateRegistryAggregator -import com.squareup.workflow1.ui.compatible -import com.squareup.workflow1.ui.getRendering - -/** - * Base class for containers that show [HasModals.modals] in [Dialog] windows. - * - * @param ModalRenderingT the type of the nested renderings to be shown in a dialog window. - */ -@WorkflowUiExperimentalApi -public abstract class ModalContainer @JvmOverloads constructor( - context: Context, - attributeSet: AttributeSet? = null, - defStyle: Int = 0, - defStyleRes: Int = 0 -) : FrameLayout(context, attributeSet, defStyle, defStyleRes) { - - private val baseViewStub: WorkflowViewStub = WorkflowViewStub(context).also { - addView(it, ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)) - } - - private var dialogs: List> = emptyList() - - /** - * Stores the result of looking for the nearest [LifecycleOwner] that should be the parent of all - * this container's modals. Only valid after the view has been attached. - */ - private val parentLifecycleOwner by lazy(mode = LazyThreadSafetyMode.NONE) { - WorkflowLifecycleOwner.get(this) ?: error( - "Expected to find either a ViewTreeLifecycleOwner in the view tree, or for the " + - "context to be a LifecycleOwner, in $this" - ) - } - - /** - * Provides a new `SavedStateRegistryOwner` for each dialog, - * which will save to the `SavedStateRegistryOwner` of this container view. - */ - private val stateRegistryAggregator = WorkflowSavedStateRegistryAggregator() - - protected fun update( - newScreen: HasModals<*, ModalRenderingT>, - viewEnvironment: ViewEnvironment - ) { - baseViewStub.update(newScreen.beneathModals, viewEnvironment) - - val newDialogs = mutableListOf>() - for ((i, modal) in newScreen.modals.withIndex()) { - newDialogs += if (i < dialogs.size && compatible(dialogs[i].modalRendering, modal)) { - dialogs[i].copy(modalRendering = modal, viewEnvironment = viewEnvironment) - .also { updateDialog(it) } - } else { - buildDialog(modal, viewEnvironment).also { ref -> - // This is the unique id we'll use to make stateRegistryAggregator put a - // SavedStateRegistryOwner on the dialog's decorView, as required by Compose. - // We put it in this sketchy lateInit field on DialogRef rather than passing - // it through abstract buildDialog method so that subclasses can't screw it up. - ref.savedStateRegistryKey = Compatible.keyFor(modal, i.toString()) - - ref.dialog.decorView?.let { dialogView -> - // Implementations of buildDialog may set their own WorkflowLifecycleOwner on the - // content view, so to avoid interfering with them we also set it here. When the views - // are attached, this will become the parent lifecycle of the one from buildDialog if - // any, and so we can use our lifecycle to destroy-on-detach the dialog hierarchy. - WorkflowLifecycleOwner.installOn( - dialogView, - onBackPressedDispatcherOwner = (ref.dialog as? OnBackPressedDispatcherOwner) - ?: viewEnvironment.onBackPressedDispatcherOwner(this), - findParentLifecycle = { parentLifecycleOwner.lifecycle } - ) - // Ensure that each dialog has its own SavedStateRegistryOwner, - // so views in each dialog layer don't clash with other layers. - stateRegistryAggregator.installChildRegistryOwnerOn( - view = dialogView, - key = ref.savedStateRegistryKey - ) - - dialogView.addOnAttachStateChangeListener( - object : OnAttachStateChangeListener { - val dismissOnDestroy = object : DefaultLifecycleObserver { - override fun onDestroy(owner: LifecycleOwner) = ref.dismiss() - } - var lifecycle: Lifecycle? = null - override fun onViewAttachedToWindow(v: View) { - // Note this is a different lifecycle than the WorkflowLifecycleOwner – it will - // probably be the owning AppCompatActivity. - lifecycle = parentLifecycleOwner.lifecycle.also { - // Android makes a lot of logcat noise if it has to close the window for us. :/ - // https://github.com/square/workflow/issues/51 - it.addObserver(dismissOnDestroy) - } - } - - override fun onViewDetachedFromWindow(v: View) { - lifecycle?.removeObserver(dismissOnDestroy) - lifecycle = null - } - } - ) - } - ref.dialog.show() - } - } - } - - (dialogs - newDialogs).forEach { it.dismiss() } - // Drop the state registries for any keys that no longer exist since the last save. - // Or really, drop everything except the remaining ones. - stateRegistryAggregator.pruneAllChildRegistryOwnersExcept( - keysToKeep = newDialogs.map { it.savedStateRegistryKey } - ) - dialogs = newDialogs - } - - /** - * Called to create (but not show) a Dialog to render [initialModalRendering]. - */ - protected abstract fun buildDialog( - initialModalRendering: ModalRenderingT, - initialViewEnvironment: ViewEnvironment - ): DialogRef - - protected abstract fun updateDialog(dialogRef: DialogRef) - - override fun onSaveInstanceState(): Parcelable { - return SavedState( - super.onSaveInstanceState()!!, - dialogs.map { it.save() } - ) - } - - override fun onRestoreInstanceState(state: Parcelable) { - (state as? SavedState) - ?.let { - if (it.dialogBundles.size == dialogs.size) { - it.dialogBundles.zip(dialogs) { viewState, dialogRef -> dialogRef.restore(viewState) } - } - super.onRestoreInstanceState(state.superState) - } - ?: super.onRestoreInstanceState(super.onSaveInstanceState()) - // ?: Some other class wrote state, but we're not allowed to skip - // the call to super. Make a no-op call. - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - val parentRegistry = stateRegistryOwnerFromViewTreeOrContext(this) - val key = Compatible.keyFor(this.getRendering()!!) - stateRegistryAggregator.attachToParentRegistry(key, parentRegistry) - } - - override fun onDetachedFromWindow() { - stateRegistryAggregator.detachFromParentRegistry() - super.onDetachedFromWindow() - } - - internal data class KeyAndBundle( - val compatibilityKey: String, - val bundle: Bundle - ) : Parcelable { - override fun describeContents(): Int = 0 - - override fun writeToParcel( - parcel: Parcel, - flags: Int - ) { - parcel.writeString(compatibilityKey) - parcel.writeBundle(bundle) - } - - companion object CREATOR : Creator { - override fun createFromParcel(parcel: Parcel): KeyAndBundle { - val key = parcel.readString()!! - val bundle = parcel.readBundle(KeyAndBundle::class.java.classLoader)!! - return KeyAndBundle(key, bundle) - } - - override fun newArray(size: Int): Array = arrayOfNulls(size) - } - } - - /** - * @param extra optional hook to allow subclasses to associate extra data with this dialog, - * e.g. its content view. Not considered for equality. - */ - @WorkflowUiExperimentalApi - protected class DialogRef( - public val modalRendering: ModalRenderingT, - public val viewEnvironment: ViewEnvironment, - public val dialog: Dialog, - public val extra: Any? = null - ) { - /** - * The unique id of the `SavedStateRegistryOwner` that will be placed - * on the dialog's decor view by [stateRegistryAggregator]. - */ - internal lateinit var savedStateRegistryKey: String - - public fun copy( - modalRendering: ModalRenderingT = this.modalRendering, - viewEnvironment: ViewEnvironment = this.viewEnvironment, - dialog: Dialog = this.dialog, - extra: Any? = this.extra - ): DialogRef = DialogRef(modalRendering, viewEnvironment, dialog, extra).also { - it.savedStateRegistryKey = savedStateRegistryKey - } - - internal fun save(): KeyAndBundle { - val saved = dialog.window!!.saveHierarchyState() - return KeyAndBundle(Compatible.keyFor(modalRendering), saved) - } - - internal fun restore(keyAndBundle: KeyAndBundle) { - if (Compatible.keyFor(modalRendering) == keyAndBundle.compatibilityKey) { - dialog.window!!.restoreHierarchyState(keyAndBundle.bundle) - } - } - - /** - * Call this instead of calling `dialog.dismiss()` directly – this method ensures that the modal's - * [WorkflowLifecycleOwner] is destroyed correctly. - */ - internal fun dismiss() { - // The dialog's views are about to be detached, and when that happens we want to transition - // the dialog view's lifecycle to a terminal state even though the parent is probably still - // alive. - dialog.decorView?.let(WorkflowLifecycleOwner::get)?.destroyOnDetach() - dialog.dismiss() - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as DialogRef<*> - - if (dialog != other.dialog) return false - - return true - } - - override fun hashCode(): Int { - return dialog.hashCode() - } - } - - private class SavedState : BaseSavedState { - constructor( - superState: Parcelable?, - dialogBundles: List - ) : super(superState) { - this.dialogBundles = dialogBundles - } - - constructor(source: Parcel) : super(source) { - @Suppress("UNCHECKED_CAST") - dialogBundles = mutableListOf().apply { - source.readTypedList(this, KeyAndBundle) - } - } - - val dialogBundles: List - - override fun writeToParcel( - out: Parcel, - flags: Int - ) { - super.writeToParcel(out, flags) - @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") - out.writeTypedList(dialogBundles) - } - - companion object CREATOR : Creator { - override fun createFromParcel(source: Parcel): SavedState = - SavedState(source) - - override fun newArray(size: Int): Array = arrayOfNulls(size) - } - } -} - -private val Dialog.decorView: View? - get() = window?.decorView diff --git a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/ModalViewContainer.kt b/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/ModalViewContainer.kt deleted file mode 100644 index d3d0b8b9a2..0000000000 --- a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/ModalViewContainer.kt +++ /dev/null @@ -1,168 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.modal - -import android.app.Dialog -import android.content.Context -import android.util.AttributeSet -import android.view.KeyEvent -import android.view.KeyEvent.ACTION_UP -import android.view.View -import android.view.ViewGroup -import android.view.ViewGroup.LayoutParams.MATCH_PARENT -import android.view.ViewGroup.LayoutParams.WRAP_CONTENT -import androidx.annotation.IdRes -import com.squareup.workflow1.ui.BuilderViewFactory -import com.squareup.workflow1.ui.Screen -import com.squareup.workflow1.ui.ScreenViewHolder -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.ViewRegistry -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.onBackPressedDispatcherOwner -import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.onBackPressedDispatcherOwnerOrNull -import com.squareup.workflow1.ui.androidx.WorkflowLifecycleOwner -import com.squareup.workflow1.ui.asScreen -import com.squareup.workflow1.ui.bindShowRendering -import com.squareup.workflow1.ui.modal.ModalViewContainer.Companion.binding -import com.squareup.workflow1.ui.navigation.BackButtonScreen -import com.squareup.workflow1.ui.show -import com.squareup.workflow1.ui.startShowing -import com.squareup.workflow1.ui.toViewFactory -import kotlin.reflect.KClass - -/** - * Container that shows [HasModals.modals] as arbitrary views in a [Dialog] - * window. Provides compatibility with - * [View.backPressedHandler][com.squareup.workflow1.ui.backPressedHandler]. - * - * Use [binding] to assign particular rendering types to be shown this way. - */ -@WorkflowUiExperimentalApi -public open class ModalViewContainer @JvmOverloads constructor( - context: Context, - attributeSet: AttributeSet? = null, - defStyle: Int = 0, - defStyleRes: Int = 0 -) : ModalContainer(context, attributeSet, defStyle, defStyleRes) { - - /** - * Called from [buildDialog]. Builds (but does not show) the [Dialog] to - * display a [view] built via [ViewRegistry]. - * - * Subclasses may override completely to build their own kind of [Dialog], - * there is no need to call `super`. - */ - public open fun buildDialogForView(view: View): Dialog { - return Dialog(context).apply { - setCancelable(false) - setContentView(view) - - // Dialog is sized to wrap the view. Note that this call must come after - // setContentView. - window!!.setLayout(WRAP_CONTENT, WRAP_CONTENT) - - // If we don't set or clear the background drawable, the window cannot go full bleed. - window!!.setBackgroundDrawable(null) - } - } - - final override fun buildDialog( - initialModalRendering: Any, - initialViewEnvironment: ViewEnvironment - ): DialogRef { - // Put a no-op backPressedHandler behind the given rendering, to - // ensure that the `onBackPressed` call below will not leak up to handlers - // that should be blocked by this modal session. - val wrappedRendering = BackButtonScreen(asScreen(initialModalRendering)) { } - - val viewHolder = wrappedRendering.toViewFactory(initialViewEnvironment) - .startShowing( - initialRendering = wrappedRendering, - initialEnvironment = initialViewEnvironment, - contextForNewView = this.context, - container = this - ) { view, doStart -> - // Note that we never call destroyOnDetach for this owner. That's okay because - // ModalContainer.update puts one in place above us on the decor view, - // and cleans it up. It's in place by the time we attach to the window, and - // so becomes our parent. - WorkflowLifecycleOwner.installOn( - view, - initialViewEnvironment.onBackPressedDispatcherOwner(view) - ) - doStart() - } - - return buildDialogForView(viewHolder.view) - .apply { - // Dialogs are modal windows and so they block events, including back button presses - // -- that's their job! But we *want* the Activity's onBackPressedDispatcher to fire - // when back is pressed, so long as it doesn't look past this modal window for handlers. - // - // Here, we handle the ACTION_UP portion of a KEYCODE_BACK key event, and below - // we make sure that the root view has a backPressedHandler that will consume the - // onBackPressed call if no child of the root modal view does. - - setOnKeyListener { _, keyCode, keyEvent -> - if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == ACTION_UP) { - viewHolder.view.onBackPressedDispatcherOwnerOrNull() - ?.onBackPressedDispatcher - ?.let { - if (it.hasEnabledCallbacks()) it.onBackPressed() - } - true - } else { - false - } - } - } - .run { - DialogRef(initialModalRendering, initialViewEnvironment, this, viewHolder) - } - } - - override fun updateDialog(dialogRef: DialogRef) { - with(dialogRef) { - // Have to preserve the wrapping done in buildDialog. (We can't put the - // BackButtonScreen in the DialogRef because the superclass needs to be - // able to do compatibility checks against it when deciding whether - // or not to update the existing dialog.) - val wrappedRendering = BackButtonScreen(asScreen(modalRendering)) { } - @Suppress("UNCHECKED_CAST") - (extra as ScreenViewHolder).show(wrappedRendering, viewEnvironment) - } - } - - @PublishedApi - internal class ModalViewFactory>( - @IdRes id: Int, - type: KClass - ) : com.squareup.workflow1.ui.ViewFactory - by BuilderViewFactory( - type = type, - viewConstructor = { initialRendering, initialEnv, context, _ -> - ModalViewContainer(context).apply { - this.id = id - layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT) - bindShowRendering(initialRendering, initialEnv, ::update) - } - } - ) - - public companion object { - /** - * Creates a [ViewFactory][com.squareup.workflow1.ui.ViewFactory] for - * modal container screens of type [H]. - * - * Each view created for [HasModals.modals] will be shown in a [Dialog] - * whose window is set to size itself to `WRAP_CONTENT` (see [android.view.Window.setLayout]). - * - * @param id a unique identifier for containers of this type, allowing them to participate - * view persistence - */ - public inline fun > binding( - @IdRes id: Int = View.NO_ID - ): com.squareup.workflow1.ui.ViewFactory = - ModalViewFactory(id, H::class) - } -} diff --git a/workflow-ui/container-android/src/main/res/values/ids.xml b/workflow-ui/container-android/src/main/res/values/ids.xml deleted file mode 100644 index 91877c61dc..0000000000 --- a/workflow-ui/container-android/src/main/res/values/ids.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/workflow-ui/container-android/src/test/java/com/squareup/workflow1/ui/modal/ModalContainerTest.kt b/workflow-ui/container-android/src/test/java/com/squareup/workflow1/ui/modal/ModalContainerTest.kt deleted file mode 100644 index 68cb977144..0000000000 --- a/workflow-ui/container-android/src/test/java/com/squareup/workflow1/ui/modal/ModalContainerTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.squareup.workflow1.ui.modal - -import android.content.Context -import android.os.Bundle -import android.os.Parcelable -import android.util.SparseArray -import android.view.View -import androidx.test.core.app.ApplicationProvider -import com.google.common.truth.Truth.assertThat -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config - -@RunWith(RobolectricTestRunner::class) -// SDK 28 required for the four-arg constructor we use in our custom view classes. -@Config(sdk = [28]) -@OptIn(WorkflowUiExperimentalApi::class) -internal class ModalContainerTest { - private val context: Context = ApplicationProvider.getApplicationContext() - - @OptIn(WorkflowUiExperimentalApi::class) - val modalContainer = object : ModalContainer(context) { - override fun buildDialog( - initialModalRendering: Any, - initialViewEnvironment: ViewEnvironment - ): DialogRef = error("unimplemented") - - override fun updateDialog(dialogRef: DialogRef) = Unit - }.apply { id = 42 } - - @Test fun ignoresAlienViewState() { - var saved = false - val weirdView = object : View(context) { - override fun onSaveInstanceState(): Parcelable { - saved = true - super.onSaveInstanceState() - return Bundle() - } - }.apply { id = 42 } - - val viewState = SparseArray() - weirdView.saveHierarchyState(viewState) - assertThat(saved).isTrue() - - modalContainer.restoreHierarchyState(viewState) - // No crash, no bug. - } -} diff --git a/workflow-ui/container-common/api/container-common.api b/workflow-ui/container-common/api/container-common.api deleted file mode 100644 index 659a56c63c..0000000000 --- a/workflow-ui/container-common/api/container-common.api +++ /dev/null @@ -1,89 +0,0 @@ -public final class com/squareup/workflow1/ui/backstack/BackStackScreen { - public fun (Ljava/lang/Object;Ljava/util/List;)V - public fun (Ljava/lang/Object;[Ljava/lang/Object;)V - public fun equals (Ljava/lang/Object;)Z - public final fun get (I)Ljava/lang/Object; - public final fun getBackStack ()Ljava/util/List; - public final fun getFrames ()Ljava/util/List; - public final fun getTop ()Ljava/lang/Object; - public fun hashCode ()I - public final fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/backstack/BackStackScreen; - public final fun mapIndexed (Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/backstack/BackStackScreen; - public final fun plus (Lcom/squareup/workflow1/ui/backstack/BackStackScreen;)Lcom/squareup/workflow1/ui/backstack/BackStackScreen; - public fun toString ()Ljava/lang/String; -} - -public final class com/squareup/workflow1/ui/backstack/BackStackScreenKt { - public static final fun asNonLegacy (Lcom/squareup/workflow1/ui/backstack/BackStackScreen;)Lcom/squareup/workflow1/ui/navigation/BackStackScreen; - public static final fun toBackStackScreen (Ljava/util/List;)Lcom/squareup/workflow1/ui/backstack/BackStackScreen; - public static final fun toBackStackScreenOrNull (Ljava/util/List;)Lcom/squareup/workflow1/ui/backstack/BackStackScreen; -} - -public final class com/squareup/workflow1/ui/modal/AlertContainerScreen : com/squareup/workflow1/ui/modal/HasModals { - public fun (Ljava/lang/Object;Lcom/squareup/workflow1/ui/modal/AlertScreen;)V - public fun (Ljava/lang/Object;Ljava/util/List;)V - public synthetic fun (Ljava/lang/Object;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Ljava/lang/Object;[Lcom/squareup/workflow1/ui/modal/AlertScreen;)V - public final fun component1 ()Ljava/lang/Object; - public final fun component2 ()Ljava/util/List; - public final fun copy (Ljava/lang/Object;Ljava/util/List;)Lcom/squareup/workflow1/ui/modal/AlertContainerScreen; - public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/modal/AlertContainerScreen;Ljava/lang/Object;Ljava/util/List;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/modal/AlertContainerScreen; - public fun equals (Ljava/lang/Object;)Z - public fun getBeneathModals ()Ljava/lang/Object; - public fun getModals ()Ljava/util/List; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/squareup/workflow1/ui/modal/AlertScreen { - public fun (Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V - public synthetic fun (Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/Map; - public final fun component2 ()Ljava/lang/String; - public final fun component3 ()Ljava/lang/String; - public final fun component4 ()Z - public final fun component5 ()Lkotlin/jvm/functions/Function1; - public final fun copy (Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/modal/AlertScreen; - public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/modal/AlertScreen;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/modal/AlertScreen; - public fun equals (Ljava/lang/Object;)Z - public final fun getButtons ()Ljava/util/Map; - public final fun getCancelable ()Z - public final fun getMessage ()Ljava/lang/String; - public final fun getOnEvent ()Lkotlin/jvm/functions/Function1; - public final fun getTitle ()Ljava/lang/String; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/squareup/workflow1/ui/modal/AlertScreen$Button : java/lang/Enum { - public static final field NEGATIVE Lcom/squareup/workflow1/ui/modal/AlertScreen$Button; - public static final field NEUTRAL Lcom/squareup/workflow1/ui/modal/AlertScreen$Button; - public static final field POSITIVE Lcom/squareup/workflow1/ui/modal/AlertScreen$Button; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public static fun valueOf (Ljava/lang/String;)Lcom/squareup/workflow1/ui/modal/AlertScreen$Button; - public static fun values ()[Lcom/squareup/workflow1/ui/modal/AlertScreen$Button; -} - -public abstract class com/squareup/workflow1/ui/modal/AlertScreen$Event { -} - -public final class com/squareup/workflow1/ui/modal/AlertScreen$Event$ButtonClicked : com/squareup/workflow1/ui/modal/AlertScreen$Event { - public fun (Lcom/squareup/workflow1/ui/modal/AlertScreen$Button;)V - public final fun component1 ()Lcom/squareup/workflow1/ui/modal/AlertScreen$Button; - public final fun copy (Lcom/squareup/workflow1/ui/modal/AlertScreen$Button;)Lcom/squareup/workflow1/ui/modal/AlertScreen$Event$ButtonClicked; - public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/modal/AlertScreen$Event$ButtonClicked;Lcom/squareup/workflow1/ui/modal/AlertScreen$Button;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/modal/AlertScreen$Event$ButtonClicked; - public fun equals (Ljava/lang/Object;)Z - public final fun getButton ()Lcom/squareup/workflow1/ui/modal/AlertScreen$Button; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/squareup/workflow1/ui/modal/AlertScreen$Event$Canceled : com/squareup/workflow1/ui/modal/AlertScreen$Event { - public static final field INSTANCE Lcom/squareup/workflow1/ui/modal/AlertScreen$Event$Canceled; -} - -public abstract interface class com/squareup/workflow1/ui/modal/HasModals { - public abstract fun getBeneathModals ()Ljava/lang/Object; - public abstract fun getModals ()Ljava/util/List; -} - diff --git a/workflow-ui/container-common/build.gradle.kts b/workflow-ui/container-common/build.gradle.kts deleted file mode 100644 index ce8950f6c6..0000000000 --- a/workflow-ui/container-common/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - id("kotlin-jvm") - id("published") -} - -dependencies { - api(libs.kotlin.jdk6) - - api(project(":workflow-ui:core-common")) - - testImplementation(libs.junit) - testImplementation(libs.kotlin.test.core) - testImplementation(libs.kotlin.test.jdk) - testImplementation(libs.truth) -} diff --git a/workflow-ui/container-common/dependencies/runtimeClasspath.txt b/workflow-ui/container-common/dependencies/runtimeClasspath.txt deleted file mode 100644 index d8cb7353d9..0000000000 --- a/workflow-ui/container-common/dependencies/runtimeClasspath.txt +++ /dev/null @@ -1,11 +0,0 @@ -com.squareup.okio:okio-jvm:3.3.0 -com.squareup.okio:okio:3.3.0 -org.jetbrains.kotlin:kotlin-bom:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib-common:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10 -org.jetbrains.kotlin:kotlin-stdlib:1.9.10 -org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3 -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -org.jetbrains:annotations:23.0.0 diff --git a/workflow-ui/container-common/gradle.properties b/workflow-ui/container-common/gradle.properties deleted file mode 100644 index cf3174e640..0000000000 --- a/workflow-ui/container-common/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -POM_ARTIFACT_ID=workflow-ui-container-common-jvm -POM_NAME=Workflow UI Container -POM_PACKAGING=jar diff --git a/workflow-ui/container-common/src/main/baseline-profile.txt b/workflow-ui/container-common/src/main/baseline-profile.txt deleted file mode 100644 index 0ac5b12112..0000000000 --- a/workflow-ui/container-common/src/main/baseline-profile.txt +++ /dev/null @@ -1,9 +0,0 @@ -HSPLcom/squareup/workflow1/ui/modal/AlertContainerScreen;->(Ljava/lang/Object;Ljava/util/List;)V -HSPLcom/squareup/workflow1/ui/modal/AlertContainerScreen;->(Ljava/lang/Object;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLcom/squareup/workflow1/ui/modal/AlertContainerScreen;->equals(Ljava/lang/Object;)Z -HSPLcom/squareup/workflow1/ui/modal/AlertContainerScreen;->getBeneathModals()Ljava/lang/Object; -HSPLcom/squareup/workflow1/ui/modal/AlertContainerScreen;->getModals()Ljava/util/List; -HSPLcom/squareup/workflow1/ui/modal/AlertContainerScreen;->toString()Ljava/lang/String; -Lcom/squareup/workflow1/ui/modal/AlertContainerScreen; -Lcom/squareup/workflow1/ui/modal/AlertScreen; -Lcom/squareup/workflow1/ui/modal/HasModals; diff --git a/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/backstack/BackStackScreen.kt b/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/backstack/BackStackScreen.kt deleted file mode 100644 index f7db909367..0000000000 --- a/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/backstack/BackStackScreen.kt +++ /dev/null @@ -1,105 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.backstack - -import com.squareup.workflow1.ui.Screen -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.asScreen -import com.squareup.workflow1.ui.navigation.BackStackScreen as NewBackStackScreen - -/** - * Represents an active screen ([top]), and a set of previously visited screens to which we may - * return ([backStack]). By rendering the entire history we allow the UI to do things like maintain - * cached view state, implement drag-back gestures without waiting for the workflow, etc. - * - * Effectively a list that can never be empty. - * - * If multiple [BackStackScreen]s are used as sibling renderings within the same parent navigation - * container (either the root activity or another [BackStackScreen]), then the siblings must be - * distinguished by wrapping them in [Named][com.squareup.workflow1.ui.Named] renderings in order to - * correctly support AndroidX `SavedStateRegistry`. - * - * @param bottom the bottom-most entry in the stack - * @param rest the rest of the stack, empty by default - */ -@WorkflowUiExperimentalApi -@Deprecated("Use com.squareup.workflow1.ui.container.BackStackScreen") -public class BackStackScreen( - bottom: StackedT, - rest: List -) { - /** - * Creates a screen with elements listed from the [bottom] to the top. - */ - public constructor( - bottom: StackedT, - vararg rest: StackedT - ) : this(bottom, rest.toList()) - - public val frames: List = listOf(bottom) + rest - - /** - * The active screen. - */ - public val top: StackedT = frames.last() - - /** - * Screens to which we may return. - */ - public val backStack: List = frames.subList(0, frames.size - 1) - - public operator fun get(index: Int): StackedT = frames[index] - - public operator fun plus(other: BackStackScreen?): BackStackScreen { - return if (other == null) { - this - } else { - BackStackScreen(frames[0], frames.subList(1, frames.size) + other.frames) - } - } - - public fun map(transform: (StackedT) -> R): BackStackScreen { - return frames.map(transform) - .toBackStackScreen() - } - - public fun mapIndexed(transform: (index: Int, StackedT) -> R): BackStackScreen { - return frames.mapIndexed(transform) - .toBackStackScreen() - } - - override fun equals(other: Any?): Boolean { - return (other as? BackStackScreen<*>)?.frames == frames - } - - override fun hashCode(): Int { - return frames.hashCode() - } - - override fun toString(): String { - return "${this::class.java.simpleName}($frames)" - } -} - -@WorkflowUiExperimentalApi -public fun List.toBackStackScreenOrNull(): BackStackScreen? = when { - isEmpty() -> null - else -> toBackStackScreen() -} - -@WorkflowUiExperimentalApi -public fun List.toBackStackScreen(): BackStackScreen { - require(isNotEmpty()) - return BackStackScreen(first(), subList(1, size)) -} - -@WorkflowUiExperimentalApi -public fun BackStackScreen<*>.asNonLegacy(): NewBackStackScreen { - return NewBackStackScreen( - bottom = asScreen(frames.first()), - rest = when (frames.size) { - 1 -> emptyList() - else -> frames.takeLast(frames.count() - 1).map { asScreen(it) } - } - ) -} diff --git a/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/modal/AlertContainerScreen.kt b/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/modal/AlertContainerScreen.kt deleted file mode 100644 index d68ade722e..0000000000 --- a/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/modal/AlertContainerScreen.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.squareup.workflow1.ui.modal - -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi - -/** - * **This will be deprecated in favor of - * [AlertOverlay][com.squareup.workflow1.ui.container.AlertOverlay] and - * [BodyAndModalsScreen][com.squareup.workflow1.ui.container.BodyAndOverlaysScreen] - * very soon.** - * - * May show a stack of [AlertScreen] over a [beneathModals]. - * - * @param B the type of [beneathModals] - */ -@Suppress("DEPRECATION") -@WorkflowUiExperimentalApi -@Deprecated( - "Use BodyAndModalsScreen and AlertOverlay", - ReplaceWith( - "BodyAndModalsScreen(beneathModals, modals)", - "com.squareup.workflow1.ui.container.BodyAndModalsScreen" - ) -) -public data class AlertContainerScreen( - override val beneathModals: B, - override val modals: List = emptyList() -) : HasModals { - public constructor( - baseScreen: B, - alert: AlertScreen - ) : this(baseScreen, listOf(alert)) - - public constructor( - baseScreen: B, - vararg alerts: AlertScreen - ) : this(baseScreen, alerts.toList()) -} diff --git a/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/modal/AlertScreen.kt b/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/modal/AlertScreen.kt deleted file mode 100644 index 8b99a51960..0000000000 --- a/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/modal/AlertScreen.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.squareup.workflow1.ui.modal - -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi - -/** - * **This will be deprecated in favor of - * [AlertOverlay][com.squareup.workflow1.ui.container.AlertOverlay] very soon.** - * - * Models a typical "You sure about that?" alert box. - */ -@WorkflowUiExperimentalApi -@Deprecated( - "Use AlertOverlay", - ReplaceWith( - "AlertOverlay(buttons, message, title, cancelable, onEvent)", - "com.squareup.workflow1.ui.container.AlertOverlay" - ) -) -public data class AlertScreen( - val buttons: Map = emptyMap(), - val message: String = "", - val title: String = "", - val cancelable: Boolean = true, - val onEvent: (Event) -> Unit -) { - public enum class Button { - POSITIVE, - NEGATIVE, - NEUTRAL - } - - public sealed class Event { - public data class ButtonClicked(val button: Button) : Event() - - public object Canceled : Event() - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - @Suppress("DEPRECATION") - other as AlertScreen - - return buttons == other.buttons && - message == other.message && - title == other.title && - cancelable == other.cancelable - } - - override fun hashCode(): Int { - var result = buttons.hashCode() - result = 31 * result + message.hashCode() - result = 31 * result + title.hashCode() - result = 31 * result + cancelable.hashCode() - return result - } -} diff --git a/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/modal/HasModals.kt b/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/modal/HasModals.kt deleted file mode 100644 index 65d46a3440..0000000000 --- a/workflow-ui/container-common/src/main/java/com/squareup/workflow1/ui/modal/HasModals.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.squareup.workflow1.ui.modal - -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi - -/** - * **This will be deprecated in favor of - * [BodyAndModalsScreen][com.squareup.workflow1.ui.container.BodyAndOverlaysScreen] - * very soon.** - * - * Interface implemented by screen classes that represent a stack of - * zero or more [modal][M] screens above a [base screen][beneathModals]. - * - * Use of this interface allows platform specific containers to share base classes, - * like `ModalContainer` in the `workflow-ui:core-android` module. - */ -@WorkflowUiExperimentalApi -@Deprecated("Use BodyAndModalsScreen") -public interface HasModals { - public val beneathModals: B - public val modals: List -} diff --git a/workflow-ui/container-common/src/test/java/com/squareup/workflow1/ui/backstack/BackStackScreenTest.kt b/workflow-ui/container-common/src/test/java/com/squareup/workflow1/ui/backstack/BackStackScreenTest.kt deleted file mode 100644 index a786e58c21..0000000000 --- a/workflow-ui/container-common/src/test/java/com/squareup/workflow1/ui/backstack/BackStackScreenTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.backstack - -import com.google.common.truth.Truth.assertThat -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import org.junit.Test -import kotlin.test.assertFailsWith - -@OptIn(WorkflowUiExperimentalApi::class) -internal class BackStackScreenTest { - @Test fun `top is last`() { - assertThat(BackStackScreen(1, 2, 3, 4).top).isEqualTo(4) - } - - @Test fun `backstack is all but top`() { - assertThat(BackStackScreen(1, 2, 3, 4).backStack).isEqualTo(listOf(1, 2, 3)) - } - - @Test fun `get works`() { - assertThat(BackStackScreen("able", "baker", "charlie")[1]).isEqualTo("baker") - } - - @Test fun `plus another stack`() { - assertThat(BackStackScreen(1, 2, 3) + BackStackScreen(8, 9, 0)) - .isEqualTo(BackStackScreen(1, 2, 3, 8, 9, 0)) - } - - @Test fun `unequal by order`() { - assertThat(BackStackScreen(1, 2, 3)).isNotEqualTo(BackStackScreen(3, 2, 1)) - } - - @Test fun `equal have matching hash`() { - assertThat(BackStackScreen(1, 2, 3).hashCode()) - .isEqualTo(BackStackScreen(1, 2, 3).hashCode()) - } - - @Test fun `unequal have mismatching hash`() { - assertThat(BackStackScreen(1, 2).hashCode()) - .isNotEqualTo(BackStackScreen(listOf(1, 2, 3)).hashCode()) - } - - @Test fun `bottom and rest`() { - assertThat( - BackStackScreen( - bottom = 1, - rest = listOf(2, 3, 4) - ) - ).isEqualTo(BackStackScreen(1, 2, 3, 4)) - } - - @Test fun singleton() { - val stack = BackStackScreen("hi") - assertThat(stack.top).isEqualTo("hi") - assertThat(stack.frames).isEqualTo(listOf("hi")) - assertThat(stack).isEqualTo(BackStackScreen("hi")) - } - - @Test fun map() { - assertThat(BackStackScreen(1, 2, 3).map { it.toString() }) - .isEqualTo(BackStackScreen("1", "2", "3")) - } - - @Test fun mapIndexed() { - val source = BackStackScreen("able", "baker", "charlie") - assertThat(source.mapIndexed { index, frame -> "$index: $frame" }) - .isEqualTo(BackStackScreen("0: able", "1: baker", "2: charlie")) - } - - @Test fun nullFromEmptyList() { - assertThat(emptyList().toBackStackScreenOrNull()).isNull() - } - - @Test fun throwFromEmptyList() { - assertFailsWith { emptyList().toBackStackScreen() } - } - - @Test fun fromList() { - assertThat(listOf(1, 2, 3).toBackStackScreen()).isEqualTo(BackStackScreen(1, 2, 3)) - } - - @Test fun fromListOrNull() { - assertThat(listOf(1, 2, 3).toBackStackScreenOrNull()).isEqualTo(BackStackScreen(1, 2, 3)) - } -} diff --git a/workflow-ui/core-android/api/core-android.api b/workflow-ui/core-android/api/core-android.api index a3c4a80f69..110e3fd8a3 100644 --- a/workflow-ui/core-android/api/core-android.api +++ b/workflow-ui/core-android/api/core-android.api @@ -11,71 +11,18 @@ public abstract interface class com/squareup/workflow1/ui/AndroidScreen : com/sq public abstract fun getViewFactory ()Lcom/squareup/workflow1/ui/ScreenViewFactory; } -public final class com/squareup/workflow1/ui/AndroidViewRegistryKt { - public static final fun buildView (Lcom/squareup/workflow1/ui/ViewRegistry;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;Lcom/squareup/workflow1/ui/ViewStarter;)Landroid/view/View; - public static synthetic fun buildView$default (Lcom/squareup/workflow1/ui/ViewRegistry;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;Lcom/squareup/workflow1/ui/ViewStarter;ILjava/lang/Object;)Landroid/view/View; - public static final fun getFactoryFor (Lcom/squareup/workflow1/ui/ViewRegistry;Lkotlin/reflect/KClass;)Lcom/squareup/workflow1/ui/ViewFactory; - public static final fun getFactoryForRendering (Lcom/squareup/workflow1/ui/ViewRegistry;Ljava/lang/Object;)Lcom/squareup/workflow1/ui/ViewFactory; -} - -public abstract interface class com/squareup/workflow1/ui/AndroidViewRendering { - public abstract fun getViewFactory ()Lcom/squareup/workflow1/ui/ViewFactory; -} - -public final class com/squareup/workflow1/ui/BackButtonScreen : com/squareup/workflow1/ui/AndroidViewRendering { - public fun (Ljava/lang/Object;ZLkotlin/jvm/functions/Function0;)V - public synthetic fun (Ljava/lang/Object;ZLkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getOnBackPressed ()Lkotlin/jvm/functions/Function0; - public final fun getShadow ()Z - public fun getViewFactory ()Lcom/squareup/workflow1/ui/ViewFactory; - public final fun getWrapped ()Ljava/lang/Object; -} - -public final class com/squareup/workflow1/ui/BackPressHandlerKt { - public static final fun getBackPressedHandler (Landroid/view/View;)Lkotlin/jvm/functions/Function0; - public static final fun setBackPressedHandler (Landroid/view/View;Lkotlin/jvm/functions/Function0;)V -} - -public final class com/squareup/workflow1/ui/BuilderViewFactory : com/squareup/workflow1/ui/ViewFactory { - public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function4;)V - public fun buildView (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; - public fun getType ()Lkotlin/reflect/KClass; -} - -public final class com/squareup/workflow1/ui/DecorativeViewFactory : com/squareup/workflow1/ui/ViewFactory { - public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/ui/ViewStarter;Lkotlin/jvm/functions/Function4;)V - public synthetic fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;Lcom/squareup/workflow1/ui/ViewStarter;Lkotlin/jvm/functions/Function4;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function2;Lcom/squareup/workflow1/ui/ViewStarter;Lkotlin/jvm/functions/Function4;)V - public synthetic fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function2;Lcom/squareup/workflow1/ui/ViewStarter;Lkotlin/jvm/functions/Function4;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun buildView (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; - public fun getType ()Lkotlin/reflect/KClass; -} - -public final class com/squareup/workflow1/ui/EditTextsKt { - public static final fun setTextChangedListener (Landroid/widget/EditText;Lkotlin/jvm/functions/Function1;)V - public static final fun updateText (Landroid/widget/EditText;Ljava/lang/CharSequence;)V -} - -public abstract interface class com/squareup/workflow1/ui/LayoutRunner { - public static final field Companion Lcom/squareup/workflow1/ui/LayoutRunner$Companion; - public abstract fun showRendering (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;)V -} - -public final class com/squareup/workflow1/ui/LayoutRunner$Companion { -} - -public final class com/squareup/workflow1/ui/LayoutRunnerViewFactory : com/squareup/workflow1/ui/ViewFactory { - public fun (Lkotlin/reflect/KClass;ILkotlin/jvm/functions/Function1;)V - public fun buildView (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; - public fun getType ()Lkotlin/reflect/KClass; -} - public final class com/squareup/workflow1/ui/LayoutScreenViewFactory : com/squareup/workflow1/ui/ScreenViewFactory { public fun (Lkotlin/reflect/KClass;ILkotlin/jvm/functions/Function1;)V public fun buildView (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Lcom/squareup/workflow1/ui/ScreenViewHolder; public fun getType ()Lkotlin/reflect/KClass; } +public final class com/squareup/workflow1/ui/OnBackPressedDispatcherOwnerKey : com/squareup/workflow1/ui/ViewEnvironmentKey { + public static final field INSTANCE Lcom/squareup/workflow1/ui/OnBackPressedDispatcherOwnerKey; + public fun getDefault ()Landroidx/activity/OnBackPressedDispatcherOwner; + public synthetic fun getDefault ()Ljava/lang/Object; +} + public final class com/squareup/workflow1/ui/ParcelableTextController : android/os/Parcelable, com/squareup/workflow1/ui/TextController { public static final field CREATOR Lcom/squareup/workflow1/ui/ParcelableTextController$CREATOR; public synthetic fun (Landroid/os/Parcel;Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -157,36 +104,22 @@ public final class com/squareup/workflow1/ui/TextControllerControlEditTextKt { public static final fun control (Lcom/squareup/workflow1/ui/TextController;Landroid/widget/EditText;)V } -public final class com/squareup/workflow1/ui/ViewBindingScreenViewFactory : com/squareup/workflow1/ui/ScreenViewFactory { - public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;)V - public fun buildView (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Lcom/squareup/workflow1/ui/ScreenViewHolder; - public fun getType ()Lkotlin/reflect/KClass; +public final class com/squareup/workflow1/ui/ViewBackHandlerKt { + public static final fun setBackHandler (Landroid/view/View;Lkotlin/jvm/functions/Function0;)V + public static final fun setBackHandler (Landroid/view/View;ZLkotlin/jvm/functions/Function0;)V + public static synthetic fun setBackHandler$default (Landroid/view/View;ZLkotlin/jvm/functions/Function0;ILjava/lang/Object;)V } -public final class com/squareup/workflow1/ui/ViewBindingViewFactory : com/squareup/workflow1/ui/ViewFactory { +public final class com/squareup/workflow1/ui/ViewBindingScreenViewFactory : com/squareup/workflow1/ui/ScreenViewFactory { public fun (Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;)V - public fun buildView (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; + public fun buildView (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Lcom/squareup/workflow1/ui/ScreenViewHolder; public fun getType ()Lkotlin/reflect/KClass; } -public abstract interface class com/squareup/workflow1/ui/ViewFactory : com/squareup/workflow1/ui/ViewRegistry$Entry { - public abstract fun buildView (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;)Landroid/view/View; -} - -public final class com/squareup/workflow1/ui/ViewFactory$DefaultImpls { - public static synthetic fun buildView$default (Lcom/squareup/workflow1/ui/ViewFactory;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;Landroid/view/ViewGroup;ILjava/lang/Object;)Landroid/view/View; -} - public final class com/squareup/workflow1/ui/ViewShowRenderingKt { - public static final fun bindShowRendering (Landroid/view/View;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;)V - public static final fun canShowRendering (Landroid/view/View;Ljava/lang/Object;)Z - public static final fun getEnvironment (Landroid/view/View;)Lcom/squareup/workflow1/ui/ViewEnvironment; public static final fun getEnvironmentOrNull (Landroid/view/View;)Lcom/squareup/workflow1/ui/ViewEnvironment; public static final fun getScreen (Landroid/view/View;)Lcom/squareup/workflow1/ui/Screen; public static final fun getScreenOrNull (Landroid/view/View;)Lcom/squareup/workflow1/ui/Screen; - public static final fun getShowRendering (Landroid/view/View;)Lkotlin/jvm/functions/Function2; - public static final fun showRendering (Landroid/view/View;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;)V - public static final fun start (Landroid/view/View;)V } public abstract interface class com/squareup/workflow1/ui/ViewStarter { @@ -198,56 +131,8 @@ public final class com/squareup/workflow1/ui/WorkflowLayout : android/widget/Fra public synthetic fun (Landroid/content/Context;Landroid/util/AttributeSet;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun show (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;)V public static synthetic fun show$default (Lcom/squareup/workflow1/ui/WorkflowLayout;Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;ILjava/lang/Object;)V - public final fun start (Landroidx/lifecycle/Lifecycle;Lkotlinx/coroutines/flow/Flow;Landroidx/lifecycle/Lifecycle$State;Lcom/squareup/workflow1/ui/ViewEnvironment;)V - public final fun start (Landroidx/lifecycle/Lifecycle;Lkotlinx/coroutines/flow/Flow;Lcom/squareup/workflow1/ui/ViewRegistry;)V - public final fun start (Lkotlinx/coroutines/flow/Flow;Lcom/squareup/workflow1/ui/ViewEnvironment;)V - public final fun start (Lkotlinx/coroutines/flow/Flow;Lcom/squareup/workflow1/ui/ViewRegistry;)V - public static synthetic fun start$default (Lcom/squareup/workflow1/ui/WorkflowLayout;Landroidx/lifecycle/Lifecycle;Lkotlinx/coroutines/flow/Flow;Landroidx/lifecycle/Lifecycle$State;Lcom/squareup/workflow1/ui/ViewEnvironment;ILjava/lang/Object;)V - public static synthetic fun start$default (Lcom/squareup/workflow1/ui/WorkflowLayout;Lkotlinx/coroutines/flow/Flow;Lcom/squareup/workflow1/ui/ViewEnvironment;ILjava/lang/Object;)V public final fun take (Landroidx/lifecycle/Lifecycle;Lkotlinx/coroutines/flow/Flow;Landroidx/lifecycle/Lifecycle$State;Lkotlin/coroutines/CoroutineContext;)V public static synthetic fun take$default (Lcom/squareup/workflow1/ui/WorkflowLayout;Landroidx/lifecycle/Lifecycle;Lkotlinx/coroutines/flow/Flow;Landroidx/lifecycle/Lifecycle$State;Lkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)V - public final fun update (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;)V -} - -public abstract class com/squareup/workflow1/ui/WorkflowViewState { - public abstract fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment; - public abstract fun getShowRendering ()Lkotlin/jvm/functions/Function2; - public abstract fun getShowing ()Ljava/lang/Object; -} - -public final class com/squareup/workflow1/ui/WorkflowViewState$New : com/squareup/workflow1/ui/WorkflowViewState { - public fun (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)V - public synthetic fun (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component2 ()Lcom/squareup/workflow1/ui/ViewEnvironment; - public final fun component3 ()Lkotlin/jvm/functions/Function2; - public final fun component4 ()Lkotlin/jvm/functions/Function1; - public final fun copy (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/WorkflowViewState$New; - public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/WorkflowViewState$New;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/WorkflowViewState$New; - public fun equals (Ljava/lang/Object;)Z - public fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment; - public fun getShowRendering ()Lkotlin/jvm/functions/Function2; - public synthetic fun getShowing ()Ljava/lang/Object; - public final fun getStarter ()Lkotlin/jvm/functions/Function1; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/squareup/workflow1/ui/WorkflowViewState$Started : com/squareup/workflow1/ui/WorkflowViewState { - public fun (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;)V - public final fun component2 ()Lcom/squareup/workflow1/ui/ViewEnvironment; - public final fun component3 ()Lkotlin/jvm/functions/Function2; - public final fun copy (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/WorkflowViewState$Started; - public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/WorkflowViewState$Started;Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/WorkflowViewState$Started; - public fun equals (Ljava/lang/Object;)Z - public fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment; - public fun getShowRendering ()Lkotlin/jvm/functions/Function2; - public synthetic fun getShowing ()Ljava/lang/Object; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/squareup/workflow1/ui/WorkflowViewStateKt { - public static final fun getWorkflowViewStateOrNull (Landroid/view/View;)Lcom/squareup/workflow1/ui/WorkflowViewState; } public final class com/squareup/workflow1/ui/WorkflowViewStub : android/view/View { @@ -270,13 +155,6 @@ public final class com/squareup/workflow1/ui/WorkflowViewStub : android/view/Vie public final fun setUpdatesVisibility (Z)V public fun setVisibility (I)V public final fun show (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;)V - public final fun update (Ljava/lang/Object;Lcom/squareup/workflow1/ui/ViewEnvironment;)Landroid/view/View; -} - -public final class com/squareup/workflow1/ui/androidx/OnBackPressedDispatcherOwnerKey : com/squareup/workflow1/ui/ViewEnvironmentKey { - public static final field INSTANCE Lcom/squareup/workflow1/ui/androidx/OnBackPressedDispatcherOwnerKey; - public fun getDefault ()Landroidx/activity/OnBackPressedDispatcherOwner; - public synthetic fun getDefault ()Ljava/lang/Object; } public final class com/squareup/workflow1/ui/androidx/WorkflowAndroidXSupport { @@ -310,32 +188,32 @@ public final class com/squareup/workflow1/ui/androidx/WorkflowSavedStateRegistry public final fun saveAndPruneChildRegistryOwner (Ljava/lang/String;)V } -public final class com/squareup/workflow1/ui/navigation/AlertDialogThemeResId : com/squareup/workflow1/ui/ViewEnvironmentKey { - public static final field INSTANCE Lcom/squareup/workflow1/ui/navigation/AlertDialogThemeResId; +public final class com/squareup/workflow1/ui/container/AlertDialogThemeResId : com/squareup/workflow1/ui/ViewEnvironmentKey { + public static final field INSTANCE Lcom/squareup/workflow1/ui/container/AlertDialogThemeResId; public fun getDefault ()Ljava/lang/Integer; public synthetic fun getDefault ()Ljava/lang/Object; } -public final class com/squareup/workflow1/ui/navigation/AlertOverlayDialogFactoryKt { - public static final fun toDialogHolder (Landroid/app/AlertDialog;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder; +public final class com/squareup/workflow1/ui/container/AlertOverlayDialogFactoryKt { + public static final fun toDialogHolder (Landroid/app/AlertDialog;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder; } -public final class com/squareup/workflow1/ui/navigation/AndroidDialogBoundsKt { +public final class com/squareup/workflow1/ui/container/AndroidDialogBoundsKt { public static final fun getScreenRect (Landroid/view/View;Landroid/graphics/Rect;)V public static final fun setBounds (Landroid/app/Dialog;Landroid/graphics/Rect;)V } -public abstract interface class com/squareup/workflow1/ui/navigation/AndroidOverlay : com/squareup/workflow1/ui/navigation/Overlay { - public abstract fun getDialogFactory ()Lcom/squareup/workflow1/ui/navigation/OverlayDialogFactory; +public abstract interface class com/squareup/workflow1/ui/container/AndroidOverlay : com/squareup/workflow1/ui/container/Overlay { + public abstract fun getDialogFactory ()Lcom/squareup/workflow1/ui/container/OverlayDialogFactory; } -public final class com/squareup/workflow1/ui/navigation/AsDialogHolderWithContentKt { - public static final fun asDialogHolderWithContent (Landroidx/activity/ComponentDialog;Lcom/squareup/workflow1/ui/navigation/ScreenOverlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder; - public static synthetic fun asDialogHolderWithContent$default (Landroidx/activity/ComponentDialog;Lcom/squareup/workflow1/ui/navigation/ScreenOverlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder; +public final class com/squareup/workflow1/ui/container/AsDialogHolderWithContentKt { + public static final fun asDialogHolderWithContent (Landroidx/activity/ComponentDialog;Lcom/squareup/workflow1/ui/container/ScreenOverlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder; + public static synthetic fun asDialogHolderWithContent$default (Landroidx/activity/ComponentDialog;Lcom/squareup/workflow1/ui/container/ScreenOverlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder; public static final fun fixBackgroundAndDimming (Landroid/app/Dialog;)V } -public final class com/squareup/workflow1/ui/navigation/BackButtonScreen : com/squareup/workflow1/ui/AndroidScreen, com/squareup/workflow1/ui/Wrapper { +public final class com/squareup/workflow1/ui/container/BackButtonScreen : com/squareup/workflow1/ui/AndroidScreen, com/squareup/workflow1/ui/Wrapper { public fun (Lcom/squareup/workflow1/ui/Screen;ZLkotlin/jvm/functions/Function0;)V public synthetic fun (Lcom/squareup/workflow1/ui/Screen;ZLkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun asSequence ()Lkotlin/sequences/Sequence; @@ -345,13 +223,12 @@ public final class com/squareup/workflow1/ui/navigation/BackButtonScreen : com/s public final fun getOnBackPressed ()Lkotlin/jvm/functions/Function0; public final fun getShadow ()Z public fun getViewFactory ()Lcom/squareup/workflow1/ui/ScreenViewFactory; - public final fun getWrapped ()Lcom/squareup/workflow1/ui/Screen; public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Container; public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Wrapper; - public fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/BackButtonScreen; + public fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/BackButtonScreen; } -public class com/squareup/workflow1/ui/navigation/BackStackContainer : android/widget/FrameLayout { +public class com/squareup/workflow1/ui/container/BackStackContainer : android/widget/FrameLayout { public fun (Landroid/content/Context;)V public fun (Landroid/content/Context;Landroid/util/AttributeSet;)V public fun (Landroid/content/Context;Landroid/util/AttributeSet;I)V @@ -362,138 +239,119 @@ public class com/squareup/workflow1/ui/navigation/BackStackContainer : android/w protected fun onRestoreInstanceState (Landroid/os/Parcelable;)V protected fun onSaveInstanceState ()Landroid/os/Parcelable; protected fun performTransition (Lcom/squareup/workflow1/ui/ScreenViewHolder;Lcom/squareup/workflow1/ui/ScreenViewHolder;Z)V - public final fun update (Lcom/squareup/workflow1/ui/navigation/BackStackScreen;Lcom/squareup/workflow1/ui/ViewEnvironment;)V + public final fun update (Lcom/squareup/workflow1/ui/container/BackStackScreen;Lcom/squareup/workflow1/ui/ViewEnvironment;)V } -public final class com/squareup/workflow1/ui/navigation/BackStackContainer$SavedState : android/view/View$BaseSavedState { - public static final field CREATOR Lcom/squareup/workflow1/ui/navigation/BackStackContainer$SavedState$CREATOR; +public final class com/squareup/workflow1/ui/container/BackStackContainer$SavedState : android/view/View$BaseSavedState { + public static final field CREATOR Lcom/squareup/workflow1/ui/container/BackStackContainer$SavedState$CREATOR; public fun (Landroid/os/Parcel;)V - public fun (Landroid/os/Parcelable;Lcom/squareup/workflow1/ui/navigation/ViewStateCache$Saved;)V - public final fun getSavedViewState ()Lcom/squareup/workflow1/ui/navigation/ViewStateCache$Saved; + public fun (Landroid/os/Parcelable;Lcom/squareup/workflow1/ui/container/ViewStateCache$Saved;)V + public final fun getSavedViewState ()Lcom/squareup/workflow1/ui/container/ViewStateCache$Saved; public fun writeToParcel (Landroid/os/Parcel;I)V } -public final class com/squareup/workflow1/ui/navigation/BackStackContainer$SavedState$CREATOR : android/os/Parcelable$Creator { - public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/navigation/BackStackContainer$SavedState; +public final class com/squareup/workflow1/ui/container/BackStackContainer$SavedState$CREATOR : android/os/Parcelable$Creator { + public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/container/BackStackContainer$SavedState; public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; - public fun newArray (I)[Lcom/squareup/workflow1/ui/navigation/BackStackContainer$SavedState; + public fun newArray (I)[Lcom/squareup/workflow1/ui/container/BackStackContainer$SavedState; public synthetic fun newArray (I)[Ljava/lang/Object; } -public final class com/squareup/workflow1/ui/navigation/LayeredDialogSessions { - public static final field Companion Lcom/squareup/workflow1/ui/navigation/LayeredDialogSessions$Companion; +public final class com/squareup/workflow1/ui/container/LayeredDialogSessions { + public static final field Companion Lcom/squareup/workflow1/ui/container/LayeredDialogSessions$Companion; public synthetic fun (Landroid/content/Context;Lkotlinx/coroutines/flow/StateFlow;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getAllowBodyEvents ()Z public final fun onAttachedToWindow (Ljava/lang/String;Landroid/view/View;)V public final fun onDetachedFromWindow ()V - public final fun onRestoreInstanceState (Lcom/squareup/workflow1/ui/navigation/LayeredDialogSessions$SavedState;)V - public final fun onSaveInstanceState ()Lcom/squareup/workflow1/ui/navigation/LayeredDialogSessions$SavedState; + public final fun onRestoreInstanceState (Lcom/squareup/workflow1/ui/container/LayeredDialogSessions$SavedState;)V + public final fun onSaveInstanceState ()Lcom/squareup/workflow1/ui/container/LayeredDialogSessions$SavedState; public final fun update (Ljava/util/List;Lcom/squareup/workflow1/ui/ViewEnvironment;Lkotlin/jvm/functions/Function1;)V } -public final class com/squareup/workflow1/ui/navigation/LayeredDialogSessions$Companion { - public final fun forView (Landroid/view/View;Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/LayeredDialogSessions; +public final class com/squareup/workflow1/ui/container/LayeredDialogSessions$Companion { + public final fun forView (Landroid/view/View;Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/LayeredDialogSessions; } -public final class com/squareup/workflow1/ui/navigation/LayeredDialogSessions$SavedState : android/os/Parcelable { - public static final field CREATOR Lcom/squareup/workflow1/ui/navigation/LayeredDialogSessions$SavedState$CREATOR; +public final class com/squareup/workflow1/ui/container/LayeredDialogSessions$SavedState : android/os/Parcelable { + public static final field CREATOR Lcom/squareup/workflow1/ui/container/LayeredDialogSessions$SavedState$CREATOR; public fun (Landroid/os/Parcel;)V public fun describeContents ()I public fun writeToParcel (Landroid/os/Parcel;I)V } -public final class com/squareup/workflow1/ui/navigation/LayeredDialogSessions$SavedState$CREATOR : android/os/Parcelable$Creator { - public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/navigation/LayeredDialogSessions$SavedState; +public final class com/squareup/workflow1/ui/container/LayeredDialogSessions$SavedState$CREATOR : android/os/Parcelable$Creator { + public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/container/LayeredDialogSessions$SavedState; public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; - public fun newArray (I)[Lcom/squareup/workflow1/ui/navigation/LayeredDialogSessions$SavedState; + public fun newArray (I)[Lcom/squareup/workflow1/ui/container/LayeredDialogSessions$SavedState; public synthetic fun newArray (I)[Ljava/lang/Object; } -public abstract interface class com/squareup/workflow1/ui/navigation/OverlayDialogFactory : com/squareup/workflow1/ui/ViewRegistry$Entry { - public static final field Companion Lcom/squareup/workflow1/ui/navigation/OverlayDialogFactory$Companion; - public abstract fun buildDialog (Lcom/squareup/workflow1/ui/navigation/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder; +public abstract interface class com/squareup/workflow1/ui/container/OverlayDialogFactory : com/squareup/workflow1/ui/ViewRegistry$Entry { + public static final field Companion Lcom/squareup/workflow1/ui/container/OverlayDialogFactory$Companion; + public abstract fun buildDialog (Lcom/squareup/workflow1/ui/container/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder; } -public final class com/squareup/workflow1/ui/navigation/OverlayDialogFactory$Companion { +public final class com/squareup/workflow1/ui/container/OverlayDialogFactory$Companion { } -public abstract interface class com/squareup/workflow1/ui/navigation/OverlayDialogFactoryFinder { - public static final field Companion Lcom/squareup/workflow1/ui/navigation/OverlayDialogFactoryFinder$Companion; - public abstract fun getDialogFactoryForRendering (Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/navigation/Overlay;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogFactory; +public abstract interface class com/squareup/workflow1/ui/container/OverlayDialogFactoryFinder { + public static final field Companion Lcom/squareup/workflow1/ui/container/OverlayDialogFactoryFinder$Companion; + public abstract fun getDialogFactoryForRendering (Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/container/Overlay;)Lcom/squareup/workflow1/ui/container/OverlayDialogFactory; } -public final class com/squareup/workflow1/ui/navigation/OverlayDialogFactoryFinder$Companion : com/squareup/workflow1/ui/ViewEnvironmentKey { - public fun getDefault ()Lcom/squareup/workflow1/ui/navigation/OverlayDialogFactoryFinder; +public final class com/squareup/workflow1/ui/container/OverlayDialogFactoryFinder$Companion : com/squareup/workflow1/ui/ViewEnvironmentKey { + public fun getDefault ()Lcom/squareup/workflow1/ui/container/OverlayDialogFactoryFinder; public synthetic fun getDefault ()Ljava/lang/Object; } -public final class com/squareup/workflow1/ui/navigation/OverlayDialogFactoryFinder$DefaultImpls { - public static fun getDialogFactoryForRendering (Lcom/squareup/workflow1/ui/navigation/OverlayDialogFactoryFinder;Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/navigation/Overlay;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogFactory; +public final class com/squareup/workflow1/ui/container/OverlayDialogFactoryFinder$DefaultImpls { + public static fun getDialogFactoryForRendering (Lcom/squareup/workflow1/ui/container/OverlayDialogFactoryFinder;Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/container/Overlay;)Lcom/squareup/workflow1/ui/container/OverlayDialogFactory; } -public final class com/squareup/workflow1/ui/navigation/OverlayDialogFactoryKt { - public static final fun toDialogFactory (Lcom/squareup/workflow1/ui/navigation/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogFactory; +public final class com/squareup/workflow1/ui/container/OverlayDialogFactoryKt { + public static final fun toDialogFactory (Lcom/squareup/workflow1/ui/container/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/container/OverlayDialogFactory; } -public abstract interface class com/squareup/workflow1/ui/navigation/OverlayDialogHolder { - public static final field Companion Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder$Companion; +public abstract interface class com/squareup/workflow1/ui/container/OverlayDialogHolder { + public static final field Companion Lcom/squareup/workflow1/ui/container/OverlayDialogHolder$Companion; public abstract fun getDialog ()Landroid/app/Dialog; public abstract fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment; - public abstract fun getOnBackPressed ()Lkotlin/jvm/functions/Function0; public abstract fun getOnUpdateBounds ()Lkotlin/jvm/functions/Function1; public abstract fun getRunner ()Lkotlin/jvm/functions/Function2; } -public final class com/squareup/workflow1/ui/navigation/OverlayDialogHolder$Companion { - public final fun invoke (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder; - public static synthetic fun invoke$default (Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder$Companion;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder; -} - -public final class com/squareup/workflow1/ui/navigation/OverlayDialogHolderKt { - public static final fun canShow (Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder;Lcom/squareup/workflow1/ui/navigation/Overlay;)Z - public static final fun getShowing (Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder;)Lcom/squareup/workflow1/ui/navigation/Overlay; - public static final fun show (Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder;Lcom/squareup/workflow1/ui/navigation/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;)V +public final class com/squareup/workflow1/ui/container/OverlayDialogHolder$Companion { + public final fun invoke (Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder; + public static synthetic fun invoke$default (Lcom/squareup/workflow1/ui/container/OverlayDialogHolder$Companion;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/app/Dialog;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/container/OverlayDialogHolder; } -public class com/squareup/workflow1/ui/navigation/ScreenOverlayDialogFactory : com/squareup/workflow1/ui/navigation/OverlayDialogFactory { - public fun (Lkotlin/reflect/KClass;)V - public synthetic fun buildDialog (Lcom/squareup/workflow1/ui/navigation/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder; - public final fun buildDialog (Lcom/squareup/workflow1/ui/navigation/ScreenOverlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Landroid/content/Context;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder; - public fun buildDialogWithContent (Lcom/squareup/workflow1/ui/navigation/ScreenOverlay;Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/ScreenViewHolder;)Lcom/squareup/workflow1/ui/navigation/OverlayDialogHolder; - public fun getType ()Lkotlin/reflect/KClass; -} - -public final class com/squareup/workflow1/ui/navigation/ScreenOverlayDialogFactoryKt { - public static final fun setContent (Landroid/app/Dialog;Lcom/squareup/workflow1/ui/ScreenViewHolder;)V -} - -public final class com/squareup/workflow1/ui/navigation/ViewSetBackHandlerKt { - public static final fun setBackHandler (Landroid/view/View;Lkotlin/jvm/functions/Function0;)V - public static final fun setBackHandler (Landroid/view/View;ZLkotlin/jvm/functions/Function0;)V - public static synthetic fun setBackHandler$default (Landroid/view/View;ZLkotlin/jvm/functions/Function0;ILjava/lang/Object;)V +public final class com/squareup/workflow1/ui/container/OverlayDialogHolderKt { + public static final fun canShow (Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;Lcom/squareup/workflow1/ui/container/Overlay;)Z + public static final fun getShowing (Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;)Lcom/squareup/workflow1/ui/container/Overlay; + public static final fun show (Lcom/squareup/workflow1/ui/container/OverlayDialogHolder;Lcom/squareup/workflow1/ui/container/Overlay;Lcom/squareup/workflow1/ui/ViewEnvironment;)V } -public final class com/squareup/workflow1/ui/navigation/ViewStateCache { +public final class com/squareup/workflow1/ui/container/ViewStateCache { public fun ()V public final fun attachToParentRegistryOwner (Ljava/lang/String;Landroidx/savedstate/SavedStateRegistryOwner;)V public final fun detachFromParentRegistry ()V public final fun prune (Ljava/util/Collection;)V - public final fun restore (Lcom/squareup/workflow1/ui/navigation/ViewStateCache$Saved;)V - public final fun save ()Lcom/squareup/workflow1/ui/navigation/ViewStateCache$Saved; + public final fun restore (Lcom/squareup/workflow1/ui/container/ViewStateCache$Saved;)V + public final fun save ()Lcom/squareup/workflow1/ui/container/ViewStateCache$Saved; public final fun update (Ljava/util/Collection;Lcom/squareup/workflow1/ui/ScreenViewHolder;Lcom/squareup/workflow1/ui/ScreenViewHolder;)V } -public final class com/squareup/workflow1/ui/navigation/ViewStateCache$Saved : android/os/Parcelable { - public static final field CREATOR Lcom/squareup/workflow1/ui/navigation/ViewStateCache$Saved$CREATOR; +public final class com/squareup/workflow1/ui/container/ViewStateCache$Saved : android/os/Parcelable { + public static final field CREATOR Lcom/squareup/workflow1/ui/container/ViewStateCache$Saved$CREATOR; public fun (Landroid/os/Parcel;)V public fun describeContents ()I public fun writeToParcel (Landroid/os/Parcel;I)V } -public final class com/squareup/workflow1/ui/navigation/ViewStateCache$Saved$CREATOR : android/os/Parcelable$Creator { - public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/navigation/ViewStateCache$Saved; +public final class com/squareup/workflow1/ui/container/ViewStateCache$Saved$CREATOR : android/os/Parcelable$Creator { + public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/container/ViewStateCache$Saved; public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; - public fun newArray (I)[Lcom/squareup/workflow1/ui/navigation/ViewStateCache$Saved; + public fun newArray (I)[Lcom/squareup/workflow1/ui/container/ViewStateCache$Saved; public synthetic fun newArray (I)[Ljava/lang/Object; } diff --git a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/BackPressedHandlerTest.kt b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/BackPressedHandlerTest.kt deleted file mode 100644 index b672850df7..0000000000 --- a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/BackPressedHandlerTest.kt +++ /dev/null @@ -1,125 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui - -import android.view.View -import android.view.ViewGroup -import androidx.activity.ComponentActivity -import androidx.activity.OnBackPressedCallback -import androidx.activity.OnBackPressedDispatcherSpy -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.Lifecycle.State.DESTROYED -import androidx.lifecycle.Lifecycle.State.RESUMED -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.LifecycleRegistry -import androidx.lifecycle.setViewTreeLifecycleOwner -import androidx.test.ext.junit.rules.ActivityScenarioRule -import com.google.common.truth.Truth.assertThat -import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule -import leakcanary.DetectLeaksAfterTestSuccess -import org.junit.Rule -import org.junit.Test -import org.junit.rules.RuleChain - -@OptIn(WorkflowUiExperimentalApi::class) -internal class BackPressedHandlerTest { - private val scenarioRule = ActivityScenarioRule(ComponentActivity::class.java) - private val scenario get() = scenarioRule.scenario - - @get:Rule val rules: RuleChain = RuleChain.outerRule(DetectLeaksAfterTestSuccess()) - .around(scenarioRule) - .around(IdlingDispatcherRule) - - private var viewHandlerCount = 0 - private val viewBackHandler: BackPressHandler = { - viewHandlerCount++ - } - - @Test fun itWorksWhenHandlerIsAddedBeforeAttach() { - scenario.onActivity { activity -> - val view = View(activity) - view.backPressedHandler = viewBackHandler - - activity.setContentView(view) - assertThat(viewHandlerCount).isEqualTo(0) - - activity.onBackPressed() - assertThat(viewHandlerCount).isEqualTo(1) - } - } - - @Test fun itWorksWhenHandlerIsAddedAfterAttach() { - scenario.onActivity { activity -> - val view = View(activity) - activity.setContentView(view) - - view.backPressedHandler = viewBackHandler - assertThat(viewHandlerCount).isEqualTo(0) - - activity.onBackPressed() - assertThat(viewHandlerCount).isEqualTo(1) - } - } - - @Test fun onlyActiveWhileViewIsAttached() { - var fallbackCallCount = 0 - val defaultBackHandler = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - fallbackCallCount++ - } - } - - scenario.onActivity { activity -> - activity.onBackPressedDispatcher.addCallback(defaultBackHandler) - - val view = View(activity) - view.backPressedHandler = viewBackHandler - - activity.onBackPressed() - assertThat(fallbackCallCount).isEqualTo(1) - assertThat(viewHandlerCount).isEqualTo(0) - - activity.setContentView(view) - activity.onBackPressed() - assertThat(fallbackCallCount).isEqualTo(1) - assertThat(viewHandlerCount).isEqualTo(1) - - (view.parent as ViewGroup).removeView(view) - activity.onBackPressed() - assertThat(fallbackCallCount).isEqualTo(2) - assertThat(viewHandlerCount).isEqualTo(1) - - activity.setContentView(view) - activity.onBackPressed() - assertThat(fallbackCallCount).isEqualTo(2) - assertThat(viewHandlerCount).isEqualTo(2) - } - } - - @Test fun callbackIsRemoved() { - scenario.onActivity { activity -> - val spy = OnBackPressedDispatcherSpy(activity.onBackPressedDispatcher) - assertThat(spy.callbacks()).isEmpty() - - val lifecycle = LifecycleRegistry(activity) - lifecycle.currentState = RESUMED - - val view = View(activity) - view.backPressedHandler = viewBackHandler - assertThat(spy.callbacks()).hasSize(1) - - val lifecycleOwner = object : LifecycleOwner { - override val lifecycle: Lifecycle - get() = lifecycle - } - view.setViewTreeLifecycleOwner(lifecycleOwner) - activity.setContentView(view) - - (view.parent as ViewGroup).removeView(view) - assertThat(spy.callbacks()).hasSize(1) - - lifecycle.currentState = DESTROYED - assertThat(spy.callbacks()).isEmpty() - } - } -} diff --git a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/DecorativeViewFactoryTest.kt b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/DecorativeViewFactoryTest.kt deleted file mode 100644 index f1cfa27c01..0000000000 --- a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/DecorativeViewFactoryTest.kt +++ /dev/null @@ -1,239 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import androidx.test.platform.app.InstrumentationRegistry -import com.google.common.truth.Truth.assertThat -import org.junit.Test - -@OptIn(WorkflowUiExperimentalApi::class) -internal class DecorativeViewFactoryTest { - private val instrumentation = InstrumentationRegistry.getInstrumentation() - - @Test fun viewStarter_is_only_call_to_showRendering() { - val events = mutableListOf() - - val innerViewFactory = object : ViewFactory { - override val type = InnerRendering::class - override fun buildView( - initialRendering: InnerRendering, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View = InnerView(contextForNewView).apply { - bindShowRendering(initialRendering, initialViewEnvironment) { rendering, _ -> - events += "inner showRendering $rendering" - } - } - } - - val envString = object : ViewEnvironmentKey() { - override val default: String get() = "Not set" - } - - val outerViewFactory = DecorativeViewFactory( - type = OuterRendering::class, - map = { outer, env -> - val enhancedEnv = env + (envString to "Updated environment") - Pair(outer.wrapped, enhancedEnv) - }, - viewStarter = { view, doStart -> - events += "viewStarter ${view.getRendering()} ${view.environment!![envString]}" - doStart() - events += "exit viewStarter" - } - ) - val viewRegistry = ViewRegistry(innerViewFactory, outerViewFactory) - val viewEnvironment = ViewEnvironment.EMPTY + viewRegistry - - viewRegistry.buildView( - OuterRendering("outer", InnerRendering("inner")), - viewEnvironment, - instrumentation.context - ).start() - - assertThat(events).containsExactly( - "viewStarter OuterRendering(outerData=outer, wrapped=InnerRendering(innerData=inner)) " + - "Updated environment", - "inner showRendering InnerRendering(innerData=inner)", - "exit viewStarter" - ) - } - - @Test fun initial_doShowRendering_calls_wrapped_showRendering() { - val events = mutableListOf() - - val innerViewFactory = object : ViewFactory { - override val type = InnerRendering::class - override fun buildView( - initialRendering: InnerRendering, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View = InnerView(contextForNewView).apply { - bindShowRendering(initialRendering, initialViewEnvironment) { rendering, _ -> - events += "inner showRendering $rendering" - } - } - } - val outerViewFactory = DecorativeViewFactory( - type = OuterRendering::class, - map = { outer -> outer.wrapped }, - doShowRendering = { _, innerShowRendering, outerRendering, env -> - events += "doShowRendering $outerRendering" - innerShowRendering(outerRendering.wrapped, env) - } - ) - val viewRegistry = ViewRegistry(innerViewFactory, outerViewFactory) - val viewEnvironment = ViewEnvironment.EMPTY + viewRegistry - - viewRegistry.buildView( - OuterRendering("outer", InnerRendering("inner")), - viewEnvironment, - instrumentation.context - ).start() - - assertThat(events).containsExactly( - "doShowRendering OuterRendering(outerData=outer, wrapped=InnerRendering(innerData=inner))", - "inner showRendering InnerRendering(innerData=inner)" - ) - } - - // https://github.com/square/workflow-kotlin/issues/597 - @Test fun double_wrapping_only_calls_showRendering_once() { - val events = mutableListOf() - - val innerViewFactory = object : ViewFactory { - override val type = InnerRendering::class - override fun buildView( - initialRendering: InnerRendering, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View = InnerView(contextForNewView).apply { - bindShowRendering(initialRendering, initialViewEnvironment) { rendering, _ -> - events += "inner showRendering $rendering" - } - } - } - - val envString = object : ViewEnvironmentKey() { - override val default: String get() = "Not set" - } - - val outerViewFactory = DecorativeViewFactory( - type = OuterRendering::class, - map = { outer, env -> - val enhancedEnv = env + ( - envString to "Outer Updated environment" + - " SHOULD NOT SEE THIS! It will be clobbered by WayOutRendering" - ) - Pair(outer.wrapped, enhancedEnv) - }, - viewStarter = { view, doStart -> - events += "outer viewStarter ${view.getRendering()} ${view.environment!![envString]}" - doStart() - events += "exit outer viewStarter" - } - ) - - val wayOutViewFactory = DecorativeViewFactory( - type = WayOutRendering::class, - map = { wayOut, env -> - val enhancedEnv = env + (envString to "Way Out Updated environment triumphs over all") - Pair(wayOut.wrapped, enhancedEnv) - }, - viewStarter = { view, doStart -> - events += "way out viewStarter ${view.getRendering()} ${view.environment!![envString]}" - doStart() - events += "exit way out viewStarter" - } - ) - val viewRegistry = ViewRegistry(innerViewFactory, outerViewFactory, wayOutViewFactory) - val viewEnvironment = ViewEnvironment.EMPTY + viewRegistry - - viewRegistry.buildView( - WayOutRendering("way out", OuterRendering("outer", InnerRendering("inner"))), - viewEnvironment, - instrumentation.context - ).start() - - assertThat(events).containsExactly( - "way out viewStarter " + - "WayOutRendering(wayOutData=way out, wrapped=" + - "OuterRendering(outerData=outer, wrapped=" + - "InnerRendering(innerData=inner))) " + - "Way Out Updated environment triumphs over all", - "outer viewStarter " + - // Notice that both the initial rendering and the ViewEnvironment are stomped by - // the outermost wrapper before inners are invoked. Could try to give - // the inner wrapper access to the rendering it expected, but there are no - // use cases and it trashes the API. - "WayOutRendering(wayOutData=way out, wrapped=" + - "OuterRendering(outerData=outer, wrapped=" + - "InnerRendering(innerData=inner))) " + - "Way Out Updated environment triumphs over all", - "inner showRendering InnerRendering(innerData=inner)", - "exit outer viewStarter", - "exit way out viewStarter" - ) - } - - @Test fun subsequent_showRendering_calls_wrapped_showRendering() { - val events = mutableListOf() - - val innerViewFactory = object : ViewFactory { - override val type = InnerRendering::class - override fun buildView( - initialRendering: InnerRendering, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View = InnerView(contextForNewView).apply { - bindShowRendering(initialRendering, initialViewEnvironment) { rendering, _ -> - events += "inner showRendering $rendering" - } - } - } - val outerViewFactory = DecorativeViewFactory( - type = OuterRendering::class, - map = { outer -> outer.wrapped }, - doShowRendering = { _, innerShowRendering, outerRendering, env -> - events += "doShowRendering $outerRendering" - innerShowRendering(outerRendering.wrapped, env) - } - ) - val viewRegistry = ViewRegistry(innerViewFactory, outerViewFactory) - val viewEnvironment = ViewEnvironment.EMPTY + viewRegistry - - val view = viewRegistry.buildView( - OuterRendering("out1", InnerRendering("in1")), - viewEnvironment, - instrumentation.context - ).apply { start() } - events.clear() - - view.showRendering(OuterRendering("out2", InnerRendering("in2")), viewEnvironment) - - assertThat(events).containsExactly( - "doShowRendering OuterRendering(outerData=out2, wrapped=InnerRendering(innerData=in2))", - "inner showRendering InnerRendering(innerData=in2)" - ) - } - - private data class InnerRendering(val innerData: String) - private data class OuterRendering( - val outerData: String, - val wrapped: InnerRendering - ) - - private data class WayOutRendering( - val wayOutData: String, - val wrapped: OuterRendering - ) - - private class InnerView(context: Context) : View(context) -} diff --git a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/EditTextsTest.kt b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/EditTextsTest.kt deleted file mode 100644 index 30f18c51d9..0000000000 --- a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/EditTextsTest.kt +++ /dev/null @@ -1,134 +0,0 @@ -package com.squareup.workflow1.ui - -import android.text.Selection -import android.text.SpannableStringBuilder -import android.widget.EditText -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.google.common.truth.Truth.assertThat -import org.junit.Assert.fail -import org.junit.Test -import org.junit.runner.RunWith - -@Suppress("DEPRECATION") -@OptIn(WorkflowUiExperimentalApi::class) -@RunWith(AndroidJUnit4::class) -internal class EditTextsTest { - - private val instrumentation = InstrumentationRegistry.getInstrumentation() - private val editText = EditText(instrumentation.context) - - @Test fun updateText_setsTextValue() { - assertThat(editText.text.toString()).isEqualTo("") - - editText.updateText("h") - assertThat(editText.text.toString()).isEqualTo("h") - - editText.updateText("hello") - assertThat(editText.text.toString()).isEqualTo("hello") - } - - @Test fun updateText_setsSelectionWhenSpecified() { - editText.setText("hello") - assertThat(editText.selectionStart).isEqualTo(0) - assertThat(editText.selectionEnd).isEqualTo(0) - - val newText = SpannableStringBuilder("hello") - .also { Selection.setSelection(it, 1, 3) } - editText.updateText(newText) - assertThat(editText.text.toString()).isEqualTo("hello") - assertThat(editText.selectionStart).isEqualTo(1) - assertThat(editText.selectionEnd).isEqualTo(3) - } - - @Test fun updateText_preservesSelectionWhenNotSpecified() { - editText.setText("hello") - editText.setSelection(1, 3) - - editText.updateText("world") - assertThat(editText.text.toString()).isEqualTo("world") - assertThat(editText.selectionStart).isEqualTo(1) - assertThat(editText.selectionEnd).isEqualTo(3) - } - - @Test fun setTextChangedListener_doesntFireImmediately() { - var fired = false - - editText.setText("hello") - editText.setTextChangedListener { fired = true } - - assertThat(fired).isFalse() - } - - @Test fun setTextChangedListener_doesntFireWhenTextChangedToInitialValue() { - var fired = false - - editText.setTextChangedListener { fired = true } - editText.setText("") - - assertThat(fired).isFalse() - } - - @Test fun setTextChangedListener_doesntFireWhenTextChangedToCurrentValue() { - var fired = false - editText.setText("hello") - - editText.setTextChangedListener { fired = true } - editText.setText("hello") - - assertThat(fired).isFalse() - } - - @Test fun setTextChangedListener_handlesTextChanges() { - val changes = mutableListOf() - editText.setTextChangedListener { changes += it.toString() } - - editText.setText("foo") - assertThat(changes).containsExactly("foo") - - editText.text!!.append("bar") - assertThat(changes).containsExactly("foo", "foobar") - } - - @Test fun setTextChangedListener_replacesPreviousListener() { - val changes = mutableListOf() - - editText.setTextChangedListener { fail("Expected original listener not to be called.") } - editText.setTextChangedListener { changes += it.toString() } - - editText.setText("foo") - assertThat(changes).containsExactly("foo") - } - - @Test fun setTextChangedListener_clearedWhenNull() { - editText.setTextChangedListener { fail("Expected original listener not to be called.") } - editText.setTextChangedListener(null) - - editText.setText("foo") - } - - @Test fun updateText_doesntTriggerTextChangedListener() { - editText.setTextChangedListener { fail("Expected updateText not to trigger listener") } - - editText.updateText("foo") - } - - @Test fun updateText_doesntTriggerInfiniteLoopInListener() { - editText.setTextChangedListener { editText.updateText(it) } - - editText.setText("foo") - - assertThat(editText.text.toString()).isEqualTo("foo") - } - - @Test fun setTextChangedListener_allowsMutatingText() { - editText.setTextChangedListener { - editText.updateText("update: $it") - editText.setTextChangedListener(null) - } - - editText.setText("foo") - - assertThat(editText.text.toString()).isEqualTo("update: foo") - } -} diff --git a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/OnBackPressedDispatcherSpy.java b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/OnBackPressedDispatcherSpy.java deleted file mode 100644 index 8163843db2..0000000000 --- a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/OnBackPressedDispatcherSpy.java +++ /dev/null @@ -1,15 +0,0 @@ -package androidx.activity; - -import java.util.ArrayDeque; - -public class OnBackPressedDispatcherSpy { - private final OnBackPressedDispatcher dispatcher; - - public OnBackPressedDispatcherSpy(OnBackPressedDispatcher dispatcher) { - this.dispatcher = dispatcher; - } - - public ArrayDeque callbacks() { - return dispatcher.mOnBackPressedCallbacks; - } -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRegistry.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRegistry.kt deleted file mode 100644 index 8472dead98..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRegistry.kt +++ /dev/null @@ -1,107 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import com.squareup.workflow1.ui.navigation.EnvironmentScreenLegacyViewFactory -import kotlin.reflect.KClass - -/** - * It is usually more convenient to use [WorkflowViewStub] or [DecorativeViewFactory] - * than to call this method directly. - * - * Returns the [ViewFactory] that builds [View] instances suitable to display the given [rendering], - * via subsequent calls to [View.showRendering]. - * - * Prefers factories found via [ViewRegistry.getFactoryFor]. If that returns null, falls - * back to the factory provided by the rendering's implementation of - * [AndroidViewRendering.viewFactory], if there is one. Note that this means that a - * compile time [AndroidViewRendering.viewFactory] binding can be overridden at runtime. - * - * @throws IllegalArgumentException if no factory can be find for type [RenderingT] - */ -@Deprecated("Use ScreenViewFactoryFinder.getViewFactoryForRendering()") -@WorkflowUiExperimentalApi -public fun ViewRegistry.getFactoryForRendering( - rendering: RenderingT -): ViewFactory { - @Suppress("UNCHECKED_CAST") - return getFactoryFor(rendering::class) - ?: (rendering as? AndroidViewRendering<*>)?.viewFactory as? ViewFactory - ?: (rendering as? Named<*>)?.let { NamedViewFactory as ViewFactory } - ?: (rendering as? AsScreen<*>)?.let { AsScreenLegacyViewFactory as ViewFactory } - ?: (rendering as? EnvironmentScreen<*>)?.let { - // Special handling to ensure the custom environment is in play before the view is built. - EnvironmentScreenLegacyViewFactory as ViewFactory - } - ?: (rendering as? Screen)?.let { LegacyFactoryForScreenType() as ViewFactory } - ?: throw IllegalArgumentException( - "A ViewFactory should have been registered to display $rendering, " + - "or that class should implement AndroidViewRendering." - ) -} - -/** - * This method is not for general use, use [WorkflowViewStub] instead. - * - * Returns the [ViewFactory] that was registered for the given [renderingType], or null - * if none was found. - */ -@Deprecated( - "Use getEntryFor()", - ReplaceWith("getEntryFor(renderingType)") -) -@WorkflowUiExperimentalApi -public fun ViewRegistry.getFactoryFor( - renderingType: KClass -): ViewFactory? { - return getEntryFor(renderingType) as? ViewFactory -} - -/** - * It is usually more convenient to use [WorkflowViewStub] or [DecorativeViewFactory] - * than to call this method directly. - * - * Finds a [ViewFactory] to create a [View] ready to display [initialRendering]. The caller - * is responsible for calling [View.start] on the new [View]. After that, - * [View.showRendering] can be used to update it with new renderings that - * are [compatible] with [initialRendering]. - * - * @param viewStarter An optional wrapper for the function invoked when [View.start] - * is called, allowing for last second initialization of a newly built [View]. - * See [ViewStarter] for details. - * - * @throws IllegalArgumentException if no factory can be found for type [RenderingT] - * - * @throws IllegalStateException if the matching [ViewFactory] fails to call - * [View.bindShowRendering] when constructing the view - */ -@Deprecated("Use Screen.toViewFactory and ScreenViewFactory.startShowing") -@WorkflowUiExperimentalApi -public fun ViewRegistry.buildView( - initialRendering: RenderingT, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? = null, - viewStarter: ViewStarter? = null, -): View { - return getFactoryForRendering(initialRendering).buildView( - initialRendering, - initialViewEnvironment, - contextForNewView, - container - ).also { view -> - checkNotNull(view.workflowViewStateOrNull) { - "View.bindShowRendering should have been called for $view, typically by the " + - "ViewFactory that created it." - } - viewStarter?.let { givenStarter -> - val doStart = view.starter - view.starter = { newView -> - givenStarter.startView(newView) { doStart.invoke(newView) } - } - } - } -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRendering.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRendering.kt deleted file mode 100644 index fccd64cb00..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AndroidViewRendering.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.squareup.workflow1.ui - -/** - * Interface implemented by a rendering class to allow it to drive an Android UI - * via an appropriate [ViewFactory] implementation. - * - * You will rarely, if ever, write a [ViewFactory] yourself. Instead - * use [LayoutRunner.bind] to work with XML layout resources, or - * [BuilderViewFactory] to create views from code. See [LayoutRunner] for more - * details. - * - * @OptIn(WorkflowUiExperimentalApi::class) - * data class HelloView( - * val message: String, - * val onClick: () -> Unit - * ) : AndroidViewRendering { - * override val viewFactory: ViewFactory = - * LayoutRunner.bind(HelloGoodbyeLayoutBinding::inflate) { r, _ -> - * helloMessage.text = r.message - * helloMessage.setOnClickListener { r.onClick() } - * } - * } - * - * This is the simplest way to bridge the gap between your workflows and the UI, - * but using it requires your workflows code to reside in Android modules, instead - * of pure Kotlin. If this is a problem, or you need more flexibility for any other - * reason, you can use [ViewRegistry] to bind your renderings to [ViewFactory] - * implementations at runtime. - */ -@Suppress("DEPRECATION") -@Deprecated("Use AndroidScreen") -@WorkflowUiExperimentalApi -public interface AndroidViewRendering> { - /** - * Used to build instances of [android.view.View] as needed to - * display renderings of this type. - */ - public val viewFactory: ViewFactory -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AsScreenLegacyViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AsScreenLegacyViewFactory.kt deleted file mode 100644 index 2951307656..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AsScreenLegacyViewFactory.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.squareup.workflow1.ui - -@Suppress("DEPRECATION") -@WorkflowUiExperimentalApi -internal object AsScreenLegacyViewFactory : ViewFactory> -by DecorativeViewFactory(AsScreen::class, { asScreen -> asScreen.rendering }) diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AsScreenViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AsScreenViewFactory.kt deleted file mode 100644 index e9e07e7c72..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/AsScreenViewFactory.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.squareup.workflow1.ui - -@Suppress("DEPRECATION") -@WorkflowUiExperimentalApi -internal fun AsScreenViewFactory( - initialRendering: AsScreen<*>, - initialViewEnvironment: ViewEnvironment -): ScreenViewFactory> { - val wrapped = initialRendering.rendering - val registry = initialViewEnvironment[ViewRegistry] - - return ScreenViewFactory.fromCode { _, environment, context, container -> - registry.buildView(wrapped, environment, context, container).let { view -> - // Capture the legacy showRendering function so that we can call it from our own - // ScreenViewHolder. - val legacyShowRendering = view.getShowRendering()!! - - // Like any legacy decorator, we need to call bindShowRendering again to - // ensure that the wrapper initialRendering is in place for View.getRendering() calls. - // Note that we're careful to preserve the ViewEnvironment put in place by the - // legacy ViewFactory - view.bindShowRendering(initialRendering, view.environment!!) { _, _ -> - // We leave a no-op (this lambda) in place for View.showRendering(), - // but ScreenViewFactory.start() will soon put something else in its place. - } - - ScreenViewHolder(environment, view) { asScreen, newEnv -> - legacyShowRendering(asScreen.rendering, newEnv) - } - } - } -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BackButtonScreen.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BackButtonScreen.kt deleted file mode 100644 index 649deef32e..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BackButtonScreen.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.squareup.workflow1.ui - -/** - * Adds optional back button handling to a [wrapped] rendering, possibly overriding that - * the wrapped rendering's own back button handler. - * - * @param shadow If `true`, [onBackPressed] is set as the - * [backPressedHandler][android.view.View.backPressedHandler] after - * the [wrapped] rendering's view is built / updated, effectively overriding it. - * If false (the default), [onBackPressed] is set afterward, to allow the wrapped rendering to - * take precedence if it sets a `backPressedHandler` of its own -- the handler provided - * here serves as a default. - * - * @param onBackPressed The function to fire when the device back button - * is pressed, or null to set no handler -- or clear a handler that was set previously. - * Defaults to `null`. - */ -@Suppress("DEPRECATION") -@WorkflowUiExperimentalApi -@Deprecated( - "Use com.squareup.workflow1.ui.navigation.BackButtonScreen", - ReplaceWith("BackButtonScreen", "com.squareup.workflow1.ui.navigation.BackButtonScreen") -) -public class BackButtonScreen( - public val wrapped: W, - public val shadow: Boolean = false, - public val onBackPressed: (() -> Unit)? = null -) : AndroidViewRendering> { - override val viewFactory: ViewFactory> = DecorativeViewFactory( - type = BackButtonScreen::class, - map = { outer -> outer.wrapped }, - doShowRendering = { view, innerShowRendering, outerRendering, viewEnvironment -> - if (!outerRendering.shadow) { - // Place our handler before invoking innerShowRendering, so that - // its later calls to view.backPressedHandler will take precedence - // over ours. - view.backPressedHandler = outerRendering.onBackPressed - } - - innerShowRendering.invoke(outerRendering.wrapped, viewEnvironment) - - if (outerRendering.shadow) { - // Place our handler after invoking innerShowRendering, so that ours wins. - view.backPressedHandler = outerRendering.onBackPressed - } - } - ) -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BackPressHandler.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BackPressHandler.kt deleted file mode 100644 index 3386b28bf4..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BackPressHandler.kt +++ /dev/null @@ -1,142 +0,0 @@ -package com.squareup.workflow1.ui - -import android.view.View -import android.view.View.OnAttachStateChangeListener -import androidx.activity.OnBackPressedCallback -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.findViewTreeLifecycleOwner -import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.onBackPressedDispatcherOwnerOrNull - -/** - * A function passed to [View.backPressedHandler], to be called if the back - * button is pressed while that view is attached to a window. - */ -@Deprecated("Use View.backHandler()") -@WorkflowUiExperimentalApi -public typealias BackPressHandler = () -> Unit - -/** - * A function to be called if the device back button is pressed while this - * view is attached to a window. - * - * Implemented via [OnBackPressedDispatcher][androidx.activity.OnBackPressedDispatcher]. - * That means that this is a last-registered-first-served mechanism, and that it is - * compatible with Compose back button handling. - */ -@Suppress("DEPRECATION") -@WorkflowUiExperimentalApi -@Deprecated("Use setBackHandler") -public var View.backPressedHandler: BackPressHandler? - get() = observerOrNull?.handler - set(value) { - observerOrNull?.stop() - - observerOrNull = value?.let { - AttachStateAndLifecycleObserver(this, it).apply { start() } - } - } - -@WorkflowUiExperimentalApi -private var View.observerOrNull: AttachStateAndLifecycleObserver? - get() = getTag(R.id.view_deprecated_back_handler) as AttachStateAndLifecycleObserver? - set(value) { - setTag(R.id.view_deprecated_back_handler, value) - } - -/** - * This is more complicated than one would hope because [Lifecycle] and memory leaks. - * - * - We need to claim our spot in the - * [OnBackPressedDispatcher][androidx.activity.OnBackPressedDispatcher] immediately, - * to ensure our [onBackPressedCallback] shadows upstream ones, and can be shadowed - * appropriately itself - * - The whole point of this mechanism is to be active only while the view is active - * - That's what [ViewTreeLifecycleOwner] is for, but we can't really find that until - * we're attached -- which often does not happen in the order required for registering - * with the dispatcher - * - * So, our [start] is called immediately, to get [onBackPressedCallback] listed at the right - * spot in the dispatcher's stack. But the [onBackPressedCallback]'s enabled / disabled state - * is tied to whether the [view] is attached or not. - * - * Also note that we expect to find a [ViewTreeLifecycleOwner] at attach time, - * so that we can know when it's time to remove the [onBackPressedCallback] from - * the dispatch stack - * ([no memory leaks please](https://github.com/square/workflow-kotlin/issues/889)). - * - * Why is it okay to wait for the [ViewTreeLifecycleOwner] to be destroyed before we - * remove [onBackPressedCallback] from the dispatcher? In normal apps that's - * the `Activity` or a `Fragment`, which will live a very long time, but Workflow UI - * is more controlling than that. `WorkflowViewStub` and the rest of the stock container - * classes use `WorkflowLifecycleOwner` to provide a short lived [ViewTreeLifecycleOwner] - * for each [View] they create, and tear it down before moving to the next one. - * - * None the less, as a belt-and-suspenders guard against leaking, - * we also take care to null out the pointer from the [onBackPressedCallback] to the - * actual [handler] while the [view] is detached. We can't be confident that the - * [ViewTreeLifecycleOwner] we find will be a well behaved one that was put in place - * by `WorkflowLifecycleOwner`. Who knows what adventures our clients will get up to. - */ -@WorkflowUiExperimentalApi -private class AttachStateAndLifecycleObserver( - private val view: View, - @Suppress("DEPRECATION") val handler: BackPressHandler -) : OnAttachStateChangeListener, DefaultLifecycleObserver { - private val onBackPressedCallback = NullableOnBackPressedCallback() - private var lifecycleOrNull: Lifecycle? = null - - fun start() { - view.onBackPressedDispatcherOwnerOrNull() - ?.let { owner -> - owner.onBackPressedDispatcher.addCallback(owner, onBackPressedCallback) - view.addOnAttachStateChangeListener(this) - if (view.isAttachedToWindow) onViewAttachedToWindow(view) - } - } - - fun stop() { - onBackPressedCallback.remove() - view.removeOnAttachStateChangeListener(this) - lifecycleOrNull?.removeObserver(this) - } - - override fun onViewAttachedToWindow(attachedView: View) { - require(view === attachedView) - lifecycleOrNull?.let { lifecycle -> - lifecycle.removeObserver(this) - lifecycleOrNull = null - } - view.findViewTreeLifecycleOwner()?.lifecycle?.let { lifecycle -> - lifecycleOrNull = lifecycle - onBackPressedCallback.handlerOrNull = handler - onBackPressedCallback.isEnabled = true - lifecycle.addObserver(this) - } - ?: error( - "Expected to find a ViewTreeLifecycleOwner to manage the " + - "backPressedHandler ($handler) for $view" - ) - } - - override fun onViewDetachedFromWindow(detachedView: View) { - require(view === detachedView) - onBackPressedCallback.isEnabled = false - onBackPressedCallback.handlerOrNull = null - } - - override fun onDestroy(owner: LifecycleOwner) { - stop() - } -} - -@WorkflowUiExperimentalApi -internal class NullableOnBackPressedCallback : OnBackPressedCallback(false) { - @Suppress("DEPRECATION") - var handlerOrNull: BackPressHandler? = null - - override fun handleOnBackPressed() { - handlerOrNull?.invoke() - } -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BuilderViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BuilderViewFactory.kt deleted file mode 100644 index c38f72b3fe..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/BuilderViewFactory.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.squareup.workflow1.ui - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import kotlin.reflect.KClass - -/** - * A [ViewFactory] that creates [View]s that need to be generated from code. - * (Use [LayoutRunner] to work with XML layout resources.) - * - * data class MyView(): AndroidViewRendering { - * val viewFactory = BuilderViewFactory( - * type = MyScreen::class, - * viewConstructor = { initialRendering, _, context, _ -> - * MyFrame(context).apply { - * layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT) - * bindShowRendering(initialRendering, ::update) - * } - * ) - * } - * - * private class MyFrame(context: Context) : FrameLayout(context, attributeSet) { - * private fun update(rendering: MyView) { ... } - * } - */ -@Suppress("DEPRECATION") -@Deprecated("Use ScreenViewFactory.fromCode") -@WorkflowUiExperimentalApi -public class BuilderViewFactory( - override val type: KClass, - private val viewConstructor: ( - initialRendering: RenderingT, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ) -> View -) : ViewFactory { - override fun buildView( - initialRendering: RenderingT, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View = viewConstructor(initialRendering, initialViewEnvironment, contextForNewView, container) -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/DecorativeViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/DecorativeViewFactory.kt deleted file mode 100644 index 234c4a5c57..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/DecorativeViewFactory.kt +++ /dev/null @@ -1,176 +0,0 @@ -package com.squareup.workflow1.ui - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import kotlin.reflect.KClass - -/** - * A [ViewFactory] for [OuterT] that delegates view construction responsibilities - * to the factory registered for [InnerT]. Makes it convenient for [OuterT] to wrap - * instances of [InnerT] to add information or behavior, without requiring wasteful wrapping - * in the view system. - * - * One general note: when creating a wrapper rendering, you're very likely to want it - * to implement [Compatible], to ensure that checks made to update or replace a view - * are based on the wrapped item. Each example below illustrates this. - * - * ## Examples - * - * To make one rendering type an "alias" for another -- that is, to use the same [ViewFactory] - * to display it -- provide nothing but a single-arg mapping function: - * - * class OriginalRendering(val data: String) : AndroidViewRendering {...} - * class AliasRendering(val similarData: String) - * - * object DecorativeViewFactory : ViewFactory - * by DecorativeViewFactory( - * type = AliasRendering::class, map = { alias -> OriginalRendering(alias.similarData) } - * ) - * - * To make a decorator type that adds information to the [ViewEnvironment]: - * - * class NeutronFlowPolarity(val reversed: Boolean) { - * companion object : ViewEnvironmentKey(NeutronFlowPolarity::class) { - * override val default: NeutronFlowPolarity = NeutronFlowPolarity(reversed = false) - * } - * } - * - * class NeutronFlowPolarityOverride( - * val wrapped: W, - * val polarity: NeutronFlowPolarity - * ) : Compatible { - * override val compatibilityKey: String = Compatible.keyFor(wrapped) - * } - * - * object NeutronFlowPolarityViewFactory : ViewFactory> - * by DecorativeViewFactory( - * type = NeutronFlowPolarityOverride::class, - * map = { override, env -> - * Pair(override.wrapped, env + (NeutronFlowPolarity to override.polarity)) - * } - * ) - * - * To make a decorator type that customizes [View] initialization: - * - * class WithTutorialTips(val wrapped: W) : Compatible { - * override val compatibilityKey: String = Compatible.keyFor(wrapped) - * } - * - * object WithTutorialTipsViewFactory : ViewFactory> - * by DecorativeViewFactory( - * type = WithTutorialTips::class, - * map = { withTips -> withTips.wrapped }, - * viewStarter = { view, doStart -> - * TutorialTipRunner.run(view) - * doStart() - * } - * ) - * - * To make a decorator type that adds pre- or post-processing to [View] updates: - * - * class BackButtonScreen( - * val wrapped: W, - * val override: Boolean = false, - * val onBackPressed: (() -> Unit)? = null - * ) : Compatible { - * override val compatibilityKey: String = Compatible.keyFor(wrapped) - * } - * - * object BackButtonViewFactory : ViewFactory> - * by DecorativeViewFactory( - * type = BackButtonScreen::class, - * map = { outer -> outer.wrapped }, - * doShowRendering = { view, innerShowRendering, outerRendering, viewEnvironment -> - * if (!outerRendering.override) { - * // Place our handler before invoking innerShowRendering, so that - * // its later calls to view.backPressedHandler will take precedence - * // over ours. - * view.backPressedHandler = outerRendering.onBackPressed - * } - * - * innerShowRendering.invoke(outerRendering.wrapped, viewEnvironment) - * - * if (outerRendering.override) { - * // Place our handler after invoking innerShowRendering, so that ours wins. - * view.backPressedHandler = outerRendering.onBackPressed - * } - * }) - * - * @param map called to convert instances of [OuterT] to [InnerT], and to - * allow [ViewEnvironment] to be transformed. - * - * @param viewStarter An optional wrapper for the function invoked when [View.start] - * is called, allowing for last second initialization of a newly built [View]. - * See [ViewStarter] for details. - * - * @param doShowRendering called to apply the [ViewShowRendering] function for - * [InnerT], allowing pre- and post-processing. Default implementation simply - * uses [map] to extract the [InnerT] instance from [OuterT] and makes the function call. - */ -@Suppress("DEPRECATION") -@Deprecated("Use ScreenViewFactory.map") -@WorkflowUiExperimentalApi -public class DecorativeViewFactory( - override val type: KClass, - private val map: (OuterT, ViewEnvironment) -> Pair, - private val viewStarter: ViewStarter? = null, - private val doShowRendering: ( - view: View, - innerShowRendering: ViewShowRendering, - outerRendering: OuterT, - env: ViewEnvironment - ) -> Unit = { _, innerShowRendering, outerRendering, viewEnvironment -> - val (innerRendering, processedEnv) = map(outerRendering, viewEnvironment) - innerShowRendering(innerRendering, processedEnv) - } -) : ViewFactory { - - /** - * Convenience constructor for cases requiring no changes to the [ViewEnvironment]. - */ - public constructor( - type: KClass, - map: (OuterT) -> InnerT, - viewStarter: ViewStarter? = null, - doShowRendering: ( - view: View, - innerShowRendering: ViewShowRendering, - outerRendering: OuterT, - env: ViewEnvironment - ) -> Unit = { _, innerShowRendering, outerRendering, viewEnvironment -> - innerShowRendering(map(outerRendering), viewEnvironment) - } - ) : this( - type, - map = { outer, viewEnvironment -> Pair(map(outer), viewEnvironment) }, - viewStarter = viewStarter, - doShowRendering = doShowRendering - ) - - override fun buildView( - initialRendering: OuterT, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View { - val (innerInitialRendering, processedInitialEnv) = map(initialRendering, initialViewEnvironment) - - return processedInitialEnv[ViewRegistry] - .buildView( - innerInitialRendering, - processedInitialEnv, - contextForNewView, - container, - viewStarter - ) - .also { view -> - val innerShowRendering: ViewShowRendering = view.getShowRendering()!! - - view.bindShowRendering( - initialRendering, - processedInitialEnv - ) { rendering, env -> doShowRendering(view, innerShowRendering, rendering, env) } - } - } -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/EditTexts.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/EditTexts.kt deleted file mode 100644 index e749d41f53..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/EditTexts.kt +++ /dev/null @@ -1,125 +0,0 @@ -package com.squareup.workflow1.ui - -import android.text.Editable -import android.text.Selection.getSelectionEnd -import android.text.Selection.getSelectionStart -import android.text.TextWatcher -import android.widget.EditText - -/** - * Helper for setting the text value of an [EditText] without disrupting the IME connection, or - * firing change listeners registered via [setTextChangedListener] if the new text is actually - * different than the old text. - * - * If [text] contains a selection, then the selection of this [EditText] is updated to it. - * - * Intended to be used by [LayoutRunner]s for updating [EditText]s from workflow renderings. - */ -@Deprecated("Use TextController instead") -@WorkflowUiExperimentalApi -public fun EditText.updateText(text: CharSequence) { - pauseTextChangedEventsRunning { - val editable = editableText - if (editable == null) { - setText(text) - } else { - editable.replace(0, editable.length, text) - } - - val textSelection = text.selection - if (textSelection != NO_SELECTION) { - setSelection(textSelection.first, textSelection.last) - } - } -} - -/** - * Helper for setting a simple function as a callback to be invoked whenever an [EditText] text - * value changes. Simpler than manually invoking [EditText.removeTextChangedListener] and - * [EditText.addTextChangedListener] and implementing a whole [TextWatcher] manually. - * - * If [listener] is not null, it will be invoked any time the text changes either due to the OS/user - * (e.g. IME connection, soft keyboard, etc.), or programmatically (i.e. `setText`), _except_ by - * calls to [updateText], which _will not_ fire this listener. It will also not fire if the text - * is technically changed, but to the same value (e.g. `setText("foo"); setText("foo")` will only - * fire the listener at most once). - * - * Intended to be used by [LayoutRunner]s for updating [EditText]s from workflow renderings. - */ -@Deprecated("Use TextController instead") -@WorkflowUiExperimentalApi -public fun EditText.setTextChangedListener(listener: ((CharSequence) -> Unit)?) { - val oldWatcher = textChangedListenerWatcher - - if (listener == null && oldWatcher != null) { - removeTextChangedListener(oldWatcher) - return - } - - if (listener != null) { - if (oldWatcher == null) { - TextChangedListenerWatcher(this, listener).also { watcher -> - textChangedListenerWatcher = watcher - addTextChangedListener(watcher) - } - } else { - oldWatcher.listener = listener - } - } -} - -private val NO_SELECTION = -1..-1 - -private val CharSequence.selection: IntRange - get() = getSelectionStart(this)..getSelectionEnd(this) - -private var EditText.textChangedListenerWatcher - get() = getTag(R.id.view_text_changed_listener) as? TextChangedListenerWatcher - set(value) { - setTag(R.id.view_text_changed_listener, value) - } - -/** - * Invokes [block], and prevents any listener set by [setTextChangedListener] from firing until - * it returns. - */ -private inline fun EditText.pauseTextChangedEventsRunning(block: () -> Unit) { - val oldWatcher = textChangedListenerWatcher?.also(::removeTextChangedListener) - block() - oldWatcher?.also(::addTextChangedListener) -} - -private class TextChangedListenerWatcher( - editText: EditText, - var listener: (CharSequence) -> Unit -) : TextWatcher { - - private var oldString: String = editText.text.toString() - - override fun onTextChanged( - newText: CharSequence, - start: Int, - before: Int, - count: Int - ) { - // Only fire the listener if the text has actually changed. - val newString = newText.toString() - if (oldString != newString) { - oldString = newString - listener(newText) - } - } - - override fun beforeTextChanged( - s: CharSequence?, - start: Int, - count: Int, - after: Int - ) { - // Noop - } - - override fun afterTextChanged(s: Editable?) { - // Noop - } -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LayoutRunner.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LayoutRunner.kt deleted file mode 100644 index 20eff621e2..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LayoutRunner.kt +++ /dev/null @@ -1,97 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui - -import android.view.View -import androidx.annotation.LayoutRes -import androidx.viewbinding.ViewBinding - -/** - * A delegate that implements a [showRendering] method to be called when a workflow rendering - * of type [RenderingT] is ready to be displayed in a view inflated from a layout resource - * by a [ViewRegistry]. (Use [BuilderViewFactory] if you want to build views from code rather - * than layouts.) - * - * If you're using [AndroidX ViewBinding][ViewBinding] you likely won't need to - * implement this interface at all. For details, see the three overloads of [LayoutRunner.bind]. - */ -@WorkflowUiExperimentalApi -@Deprecated("Use ScreenViewRunner") -public fun interface LayoutRunner { - public fun showRendering( - rendering: RenderingT, - viewEnvironment: ViewEnvironment - ) - - public companion object { - /** - * Creates a [ViewFactory] that [inflates][bindingInflater] a [ViewBinding] ([BindingT]) - * to show renderings of type [RenderingT], using [a lambda][showRendering]. - * - * val HelloBinding: ViewFactory = - * LayoutRunner.bind(HelloGoodbyeLayoutBinding::inflate) { rendering, viewEnvironment -> - * helloMessage.text = rendering.message - * helloMessage.setOnClickListener { rendering.onClick(Unit) } - * } - * - * If you need to initialize your view before [showRendering] is called, - * implement [LayoutRunner] and create a binding using the `bind` variant - * that accepts a `(ViewBinding) -> LayoutRunner` function, below. - */ - public inline fun bind( - noinline bindingInflater: ViewBindingInflater, - crossinline showRendering: BindingT.(RenderingT, ViewEnvironment) -> Unit - ): ViewFactory = bind(bindingInflater) { binding -> - LayoutRunner { rendering, viewEnvironment -> - binding.showRendering(rendering, viewEnvironment) - } - } - - /** - * Creates a [ViewFactory] that [inflates][bindingInflater] a [ViewBinding] ([BindingT]) - * to show renderings of type [RenderingT], using a [LayoutRunner] created by [constructor]. - * Handy if you need to perform some set up before [showRendering] is called. - * - * class HelloLayoutRunner( - * private val binding: HelloGoodbyeLayoutBinding - * ) : LayoutRunner { - * - * override fun showRendering(rendering: Rendering) { - * binding.messageView.text = rendering.message - * binding.messageView.setOnClickListener { rendering.onClick(Unit) } - * } - * - * companion object : ViewFactory by bind( - * HelloGoodbyeLayoutBinding::inflate, ::HelloLayoutRunner - * ) - * } - * - * If the view doesn't need to be initialized before [showRendering] is called, - * use the variant above which just takes a lambda. - */ - public inline fun bind( - noinline bindingInflater: ViewBindingInflater, - noinline constructor: (BindingT) -> LayoutRunner - ): ViewFactory = - ViewBindingViewFactory(RenderingT::class, bindingInflater, constructor) - - /** - * Creates a [ViewFactory] that inflates [layoutId] to show renderings of type [RenderingT], - * using a [LayoutRunner] created by [constructor]. Avoids any use of - * [AndroidX ViewBinding][ViewBinding]. - */ - public inline fun bind( - @LayoutRes layoutId: Int, - noinline constructor: (View) -> LayoutRunner - ): ViewFactory = LayoutRunnerViewFactory(RenderingT::class, layoutId, constructor) - - /** - * Creates a [ViewFactory] that inflates [layoutId] to "show" renderings of type [RenderingT], - * with a no-op [LayoutRunner]. Handy for showing static views, e.g. when prototyping. - */ - @Suppress("unused") - public inline fun bindNoRunner( - @LayoutRes layoutId: Int - ): ViewFactory = bind(layoutId) { LayoutRunner { _, _ -> } } - } -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LayoutRunnerViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LayoutRunnerViewFactory.kt deleted file mode 100644 index e471e19956..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LayoutRunnerViewFactory.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.squareup.workflow1.ui - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import androidx.annotation.LayoutRes -import kotlin.reflect.KClass - -/** - * A [ViewFactory] that ties a [layout resource][layoutId] to a - * [LayoutRunner factory][runnerConstructor] function. See [LayoutRunner] for - * details. - */ -@Suppress("DEPRECATION") -@WorkflowUiExperimentalApi -@PublishedApi -internal class LayoutRunnerViewFactory( - override val type: KClass, - @LayoutRes private val layoutId: Int, - private val runnerConstructor: (View) -> LayoutRunner -) : ViewFactory { - override fun buildView( - initialRendering: RenderingT, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View { - return contextForNewView.viewBindingLayoutInflater(container) - .inflate(layoutId, container, false) - .also { view -> - val runner = runnerConstructor(view) - view.bindShowRendering(initialRendering, initialViewEnvironment) { rendering, environment -> - runner.showRendering(rendering, environment) - } - } - } -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LegacyFactoryForScreenType.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LegacyFactoryForScreenType.kt deleted file mode 100644 index 203a2914cb..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/LegacyFactoryForScreenType.kt +++ /dev/null @@ -1,31 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui - -import android.content.Context -import android.view.View -import android.view.ViewGroup - -@WorkflowUiExperimentalApi -internal class LegacyFactoryForScreenType : ViewFactory { - override val type = Screen::class - - override fun buildView( - initialRendering: Screen, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View { - val modernFactory = initialRendering.toViewFactory(initialViewEnvironment) - val holder = modernFactory.buildView( - initialRendering, - initialViewEnvironment, - contextForNewView, - container - ) - holder.view.bindShowRendering(initialRendering, initialViewEnvironment) { r, e -> - holder.runner.showRendering(r, e) - } - return holder.view - } -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/NamedViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/NamedViewFactory.kt deleted file mode 100644 index 3349140614..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/NamedViewFactory.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.squareup.workflow1.ui - -/** - * [ViewFactory] that allows views to display instances of [Named]. Delegates - * to the factory for [Named.wrapped]. - */ -@Suppress("DEPRECATION") -@WorkflowUiExperimentalApi -internal object NamedViewFactory : ViewFactory> -by DecorativeViewFactory(Named::class, { named -> named.wrapped }) diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewFactory.kt index 10fefc65cf..e0fab7089c 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewFactory.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewFactory.kt @@ -20,7 +20,7 @@ public typealias ViewBindingInflater = (LayoutInflater, ViewGroup?, Bo * * Use [fromLayout], [fromViewBinding], etc., to create a [ScreenViewFactory]. These helper methods * take a layout resource, view binding, or view building function as arguments, along with a - * factory to create a [showRendering] [ScreenViewRunner.showRendering] function. + * factory to create a [ScreenViewRunner.showRendering] function. * * It is rare to call [buildView] directly. Instead the most common path is to pass [Screen] * instances to [WorkflowViewStub.show], which will apply the [ScreenViewFactory] machinery for you. @@ -63,7 +63,7 @@ public interface ScreenViewFactory : ViewRegistry.Entry = * forViewBinding(HelloGoodbyeViewBinding::inflate) { rendering, _ -> helloMessage.text @@ -97,8 +97,8 @@ public interface ScreenViewFactory : ViewRegistry.Entry by forViewBinding( * HelloGoodbyeViewBinding::inflate, ::HelloScreenRunner ) } * - * If the view doesn't need to be initialized before [showRendering] is called, use the variant - * above which just takes a lambda. + * If the view doesn't need to be initialized before [ScreenViewRunner.showRendering] + * is called, use the variant above which just takes a lambda. */ public inline fun fromViewBinding( noinline bindingInflater: ViewBindingInflater, @@ -292,7 +292,6 @@ public fun ScreenT.toViewFactory( * [WorkflowLifecycleOwner.installOn][com.squareup.workflow1.ui.androidx.WorkflowLifecycleOwner.installOn]), * provide a [viewStarter]. */ -@Suppress("DEPRECATION") @WorkflowUiExperimentalApi public fun ScreenViewHolder.startShowing( initialRendering: ScreenT, @@ -301,47 +300,13 @@ public fun ScreenViewHolder.startShowing( ) { val resolvedStarter = viewStarter ?: ViewStarter { _, doStart -> doStart() } - val legacyStarter: ((View) -> Unit)? = view.starterOrNull - - if (legacyStarter != null) { - var shown = false - // This View was built by a legacy ViewFactory, and so it needs to be - // started in just the right way. - // - // The tricky bit is the old starter's default value, a function that calls - // View.showRendering(). Odds are it's wrapped and wrapped again deep inside - // legacyStarter. To ensure it gets called at the right time, and that we don't - // update the view redundantly, we use bindShowRendering to replace View.showRendering() - // with a call to our own holder.show(). (No need to call the original showRendering(), - // AsScreenViewFactory blanked it.) - // - // This same call to bindShowRendering will also update View.getRendering() and - // View.environment() to return what was passed in here, as expected. - view.bindShowRendering( - initialRendering, - initialEnvironment - ) { rendering, environment -> - show(rendering, environment) - shown = true - } - view.starter = { startingView -> - resolvedStarter.startView(startingView) { legacyStarter(startingView) } - } - // We have to call View.start() to fire this off rather than calling the starter directly, - // to keep the rest of the legacy machinery happy. - view.start() - check(shown) { - "$viewStarter neglected to call the given doStart() function when showing $initialRendering" - } - } else { - var shown = false - resolvedStarter.startView(view) { - show(initialRendering, initialEnvironment) - shown = true - } - check(shown) { - "$viewStarter neglected to call the given doStart() function when showing $initialRendering" - } + var shown = false + resolvedStarter.startView(view) { + show(initialRendering, initialEnvironment) + shown = true + } + check(shown) { + "$viewStarter neglected to call the given doStart() function when showing $initialRendering" } } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewFactoryFinder.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewFactoryFinder.kt index 94d43a8558..89c319a8f4 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewFactoryFinder.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ScreenViewFactoryFinder.kt @@ -58,9 +58,6 @@ public interface ScreenViewFactoryFinder { @Suppress("UNCHECKED_CAST") return (entry as? ScreenViewFactory) ?: (rendering as? AndroidScreen<*>)?.viewFactory as? ScreenViewFactory - ?: (rendering as? AsScreen<*>)?.let { - AsScreenViewFactory(it, environment) as ScreenViewFactory - } ?: (rendering as? BackStackScreen<*>)?.let { BackStackScreenViewFactory as ScreenViewFactory } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewBindingViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewBindingViewFactory.kt deleted file mode 100644 index 94c62f177c..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewBindingViewFactory.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.squareup.workflow1.ui - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import androidx.viewbinding.ViewBinding -import kotlin.reflect.KClass - -@Suppress("DEPRECATION") -@WorkflowUiExperimentalApi -@PublishedApi -internal class ViewBindingViewFactory( - override val type: KClass, - private val bindingInflater: ViewBindingInflater, - private val runnerConstructor: (BindingT) -> LayoutRunner -) : ViewFactory { - override fun buildView( - initialRendering: RenderingT, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View = - bindingInflater(contextForNewView.viewBindingLayoutInflater(container), container, false) - .also { binding -> - val runner = runnerConstructor(binding) - binding.root.bindShowRendering( - initialRendering, - initialViewEnvironment - ) { rendering, environment -> - runner.showRendering(rendering, environment) - } - } - .root -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewFactory.kt deleted file mode 100644 index 4a0810d8b8..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewFactory.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.squareup.workflow1.ui - -import android.content.Context -import android.view.View -import android.view.ViewGroup - -/** - * Factory for [View] instances that can show renderings of type[RenderingT]. - * - * Two concrete [ViewFactory] implementations are provided: - * - * - The various [bind][LayoutRunner.bind] methods on [LayoutRunner] allow easy use of - * Android XML layout resources and [AndroidX ViewBinding][androidx.viewbinding.ViewBinding]. - * - * - [BuilderViewFactory] allows views to be built from code. - * - * It's simplest to have your rendering classes implement [AndroidViewRendering] to associate - * them with appropriate an appropriate [ViewFactory]. For more flexibility, and to - * avoid coupling your workflow directly to the Android runtime, see [ViewRegistry]. - */ -@Deprecated("Use ScreenViewFactory") -@WorkflowUiExperimentalApi -public interface ViewFactory : ViewRegistry.Entry { - /** - * Returns a View ready to display [initialRendering] (and any succeeding values) - * via [View.showRendering]. - */ - public fun buildView( - initialRendering: RenderingT, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? = null - ): View -} diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewShowRendering.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewShowRendering.kt index 9234e25b43..273187f195 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewShowRendering.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewShowRendering.kt @@ -1,151 +1,11 @@ package com.squareup.workflow1.ui import android.view.View -import com.squareup.workflow1.ui.WorkflowViewState.New -import com.squareup.workflow1.ui.WorkflowViewState.Started - -/** - * Function attached to a view created by [ViewFactory], to allow it - * to respond to [View.showRendering]. - */ -@WorkflowUiExperimentalApi -public typealias ViewShowRendering = - (@UnsafeVariance RenderingT, ViewEnvironment) -> Unit -// Unsafe because typealias ViewShowRendering is not supported, can't -// declare variance on a typealias. If I recall correctly. - -/** - * For use by implementations of [ViewFactory.buildView]. Establishes [showRendering] - * as the implementation of [View.showRendering] for the receiver, possibly replacing - * the existing one. - * - * - After this method is called, [View.start] must be called exactly - * once before [View.showRendering] can be called. - * - If this method is called again _after_ [View.start] (e.g. if a [View] is reused), - * the receiver is reset to its initialized state, and [View.start] must - * be called again. - * - * @see ViewFactory - * @see DecorativeViewFactory - */ -@Suppress("DeprecatedCallableAddReplaceWith") -@WorkflowUiExperimentalApi -@Deprecated("Replaced by ScreenViewHolder") -public fun View.bindShowRendering( - initialRendering: RenderingT, - initialViewEnvironment: ViewEnvironment, - showRendering: ViewShowRendering -) { - workflowViewState = when (workflowViewStateOrNull) { - is New<*> -> New(initialRendering, initialViewEnvironment, showRendering, starter) - else -> New(initialRendering, initialViewEnvironment, showRendering) - } - - // Note that if there is already a `New<*>` tag, we have to take care to propagate - // the starter. Repeated calls happen whenever one ViewFactory delegates to another. - // - // - We render `NamedScreen(FooScreen())` - // - The view is built by `FooScreenFactory`, which calls `bindShowRendering()` - // - `NamedScreenFactory` invokes `FooScreenFactory.buildView`, and calls - // `bindShowRendering>()` on the view that `FooScreenFactory` built. -} - -/** - * Note that [WorkflowViewStub] calls this method for you. - * - * Makes the initial call to [View.showRendering], along with any wrappers that have been - * added via [ViewRegistry.buildView], or [DecorativeViewFactory.viewStarter]. - * - * - It is an error to call this method more than once. - * - It is an error to call [View.showRendering] without having called this method first. - */ -@WorkflowUiExperimentalApi -@Deprecated("Use ScreenViewFactory.startShowing to create a ScreenViewHolder") -public fun View.start() { - val current = workflowViewStateAsNew - workflowViewState = Started(current.showing, current.environment, current.showRendering) - current.starter(this) -} - -/** - * Note that [WorkflowViewStub.showRendering] makes this check for you. - * - * True if this view is able to show [rendering]. - * - * Returns `false` if [View.bindShowRendering] has not been called, so it is always safe to - * call this method. Otherwise returns the [compatibility][compatible] of the current - * [View.getRendering] and the new one. - */ -@WorkflowUiExperimentalApi -@Deprecated("Replaced by ScreenViewHolder.canShow") -public fun View.canShowRendering(rendering: Any): Boolean { - @Suppress("DEPRECATION") - return getRendering()?.let { compatible(it, rendering) } == true -} - -/** - * It is usually more convenient to call [WorkflowViewStub.showRendering] - * than to call this method directly. - * - * Shows [rendering] in this View by invoking the [ViewShowRendering] function - * previously set by [bindShowRendering]. - * - * @throws IllegalStateException if [bindShowRendering] has not been called. - */ -@WorkflowUiExperimentalApi -@Deprecated("Replaced by ScreenViewHolder.show") -public fun View.showRendering( - rendering: RenderingT, - viewEnvironment: ViewEnvironment -) { - workflowViewStateAsStarted.let { viewState -> - check(compatible(viewState.showing, rendering)) { - "Expected $this to be able to show rendering $rendering, but that did not match " + - "previous rendering ${viewState.showing}. " + - "Consider using WorkflowViewStub to display arbitrary types." - } - - // Update the tag's rendering and viewEnvironment before calling - // the actual showRendering function. Note that we update both the - // new workflow_screen tag and the old workflowViewState backing View.getRendering(). - screen = asScreen(rendering) - workflowViewState = Started(rendering, viewEnvironment, viewState.showRendering) - - viewState.showRendering.invoke(rendering, viewEnvironment) - } -} - -/** - * Returns the most recent rendering [shown][showRendering] by this [View], cast to [RenderingT]; - * or null if [bindShowRendering] has never been called. - * - * Note that this is tied strictly to calls to [showRendering], and does not reflect - * calls to [ScreenViewHolder.show], which is poised to replace that function. For - * reliable access to the latest rendering displayed by a View, use the [screenOrNull]. - * - * @throws ClassCastException if the current rendering is not of type [RenderingT] - */ -@WorkflowUiExperimentalApi -@Deprecated( - "Replaced by View.screen", - ReplaceWith( - "screen", - "com.squareup.workflow1.ui.screen", - ) -) -public inline fun View.getRendering(): RenderingT? { - // Can't use a val because of the parameter type. - return when (val showing = workflowViewStateOrNull?.showing) { - null -> null - else -> showing as RenderingT - } -} /** * Returns the most recent [Screen] rendering [shown][ScreenViewHolder.show] in this view, * or throws a [NullPointerException] if the receiver was not created via - * [ScreenViewFactory.startShowing]. If the receiver is showing non-[Screen] - * rendering set by the legacy [showRendering], it will be returned wrapped in [AsScreen]. + * [ScreenViewFactory.startShowing]. */ @WorkflowUiExperimentalApi public var View.screen: Screen @@ -154,53 +14,16 @@ public var View.screen: Screen /** * Returns the most recent [Screen] rendering [shown][ScreenViewHolder.show] in this view, - * or `null` if the receiver was not created via [ScreenViewFactory.startShowing]. If - * the receiver is showing non-[Screen] rendering set by the legacy [showRendering], - * it will be returned wrapped in [AsScreen]. + * or `null` if the receiver was not initialized via [ScreenViewHolder.startShowing]. */ @WorkflowUiExperimentalApi public val View.screenOrNull: Screen? get() = getTag(R.id.workflow_screen) as? Screen /** - * Returns the most recent [ViewEnvironment] applied to this view, or null if [bindShowRendering] - * has never been called. - */ -@WorkflowUiExperimentalApi -@Deprecated( - "Replaced by View.environmentOrNull", - ReplaceWith("environmentOrNull", "com.squareup.workflow1.ui.environmentOrNull") -) -public val View.environment: ViewEnvironment? - get() = environmentOrNull - -/** - * Returns the most recent [ViewEnvironment] applied to this view, or null if [bindShowRendering] - * has never been called. + * Returns the most recent [ViewEnvironment] applied to this view, or null + * if the receiver was not initialized via [ScreenViewHolder.startShowing]. */ @WorkflowUiExperimentalApi public val View.environmentOrNull: ViewEnvironment? - get() = workflowViewStateOrNull?.environment - ?: getTag(R.id.workflow_environment) as? ViewEnvironment - -/** - * Returns the function set by the most recent call to [bindShowRendering], or null - * if that method has never been called. - */ -@Suppress("DeprecatedCallableAddReplaceWith") -@WorkflowUiExperimentalApi -@Deprecated("Replaced by ScreenViewHolder") -public fun View.getShowRendering(): ViewShowRendering? { - return workflowViewStateOrNull?.showRendering -} - -@WorkflowUiExperimentalApi -internal var View.starter: (View) -> Unit - get() = workflowViewStateAsNew.starter - set(value) { - workflowViewState = workflowViewStateAsNew.copy(starter = value) - } - -@WorkflowUiExperimentalApi -internal val View.starterOrNull: ((View) -> Unit)? - get() = (workflowViewStateOrNull as? New<*>)?.starter + get() = getTag(R.id.workflow_environment) as? ViewEnvironment diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowLayout.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowLayout.kt index 1da70805d3..aab9ab160b 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowLayout.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowLayout.kt @@ -7,7 +7,6 @@ import android.os.Parcelable import android.os.Parcelable.Creator import android.util.AttributeSet import android.util.SparseArray -import android.view.View import android.widget.FrameLayout import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle.State @@ -17,12 +16,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.squareup.workflow1.ui.androidx.OnBackPressedDispatcherOwnerKey import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.onBackPressedDispatcherOwnerOrNull import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -114,116 +108,6 @@ public class WorkflowLayout( } } - @Deprecated( - "Use show", - ReplaceWith( - "show(asScreen(newRendering).withEnvironment(environment))", - "com.squareup.workflow1.ui.asScreen", - "com.squareup.workflow1.ui.withEnvironment", - ) - ) - public fun update( - newRendering: Any, - environment: ViewEnvironment - ) { - @Suppress("DEPRECATION") - showing.update(newRendering, environment.withOnBackDispatcher()) - restoredChildState?.let { restoredState -> - restoredChildState = null - showing.actual.restoreHierarchyState(restoredState) - } - } - - @Deprecated( - "Use take()", - ReplaceWith( - "take(lifecycle, " + - "renderings.map { asScreen(it).withEnvironment(environment) }, " + - "repeatOnLifecycle)", - "com.squareup.workflow1.ui.ViewEnvironment", - "com.squareup.workflow1.ui.ViewRegistry", - "com.squareup.workflow1.ui.asScreen", - "com.squareup.workflow1.ui.withEnvironment", - "kotlinx.coroutines.flow.map" - ) - ) - @Suppress("DEPRECATION") - public fun start( - lifecycle: Lifecycle, - renderings: Flow, - repeatOnLifecycle: State = STARTED, - environment: ViewEnvironment = ViewEnvironment.EMPTY - ) { - // Just like https://medium.com/androiddevelopers/a-safer-way-to-collect-flows-from-android-uis-23080b1f8bda - lifecycle.coroutineScope.launch { - lifecycle.repeatOnLifecycle(repeatOnLifecycle) { - renderings.collect { update(it, environment) } - } - } - } - - @Deprecated( - "Use take()", - ReplaceWith( - "take(lifecycle, renderings.map { asScreen(it).withRegistry(registry) })", - "com.squareup.workflow1.ui.ViewEnvironment", - "com.squareup.workflow1.ui.ViewRegistry", - "com.squareup.workflow1.ui.asScreen", - "com.squareup.workflow1.ui.withRegistry", - "kotlinx.coroutines.flow.map" - ) - ) - public fun start( - lifecycle: Lifecycle, - renderings: Flow, - registry: ViewRegistry - ) { - @Suppress("DEPRECATION") - start( - lifecycle = lifecycle, - renderings = renderings, - environment = ViewEnvironment(mapOf(ViewRegistry to registry)) - ) - } - - @Deprecated( - "Use take()", - ReplaceWith( - "take(lifecycle, renderings.map { asScreen(it).withEnvironment(environment) })", - "com.squareup.workflow1.ui.ViewEnvironment", - "com.squareup.workflow1.ui.ViewRegistry", - "com.squareup.workflow1.ui.asScreen", - "com.squareup.workflow1.ui.withEnvironment", - "kotlinx.coroutines.flow.map" - ) - ) - @Suppress("DEPRECATION") - public fun start( - renderings: Flow, - environment: ViewEnvironment = ViewEnvironment.EMPTY - ) { - takeWhileAttached(renderings) { update(it, environment) } - } - - @Deprecated( - "Use take()", - ReplaceWith( - "take(lifecycle, renderings.map { asScreen(it).withRegistry(registry) })", - "com.squareup.workflow1.ui.ViewEnvironment", - "com.squareup.workflow1.ui.ViewRegistry", - "com.squareup.workflow1.ui.asScreen", - "com.squareup.workflow1.ui.withRegistry", - "kotlinx.coroutines.flow.map" - ) - ) - public fun start( - renderings: Flow, - registry: ViewRegistry - ) { - @Suppress("DEPRECATION") - start(renderings, ViewEnvironment(mapOf(ViewRegistry to registry))) - } - override fun onSaveInstanceState(): Parcelable { return SavedState( super.onSaveInstanceState()!!, @@ -293,32 +177,4 @@ public class WorkflowLayout( override fun newArray(size: Int): Array = arrayOfNulls(size) } } - - /** - * Subscribes [update] to [source] only while this [View] is attached to a window. - * Deprecated, leads to redundant calls to OnAttachStateChangeListener.onViewAttachedToWindow. - * To be deleted along with its callers. - */ - @Deprecated("Do not use.") - private fun View.takeWhileAttached( - source: Flow, - update: (S) -> Unit - ) { - val listener = object : OnAttachStateChangeListener { - val scope = CoroutineScope(Dispatchers.Main.immediate) - var job: Job? = null - - override fun onViewAttachedToWindow(v: View) { - job = source.onEach { screen -> update(screen) } - .launchIn(scope) - } - - override fun onViewDetachedFromWindow(v: View) { - job?.cancel() - job = null - } - } - - this.addOnAttachStateChangeListener(listener) - } } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewState.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewState.kt deleted file mode 100644 index d9518c7d8f..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewState.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.squareup.workflow1.ui - -import android.view.View -import com.squareup.workflow1.ui.WorkflowViewState.New -import com.squareup.workflow1.ui.WorkflowViewState.Started - -/** - * [View tag][View.setTag] that holds the functions and state backing [View.showRendering], etc. - */ -@WorkflowUiExperimentalApi -@PublishedApi -internal sealed class WorkflowViewState { - @PublishedApi - internal abstract val showing: RenderingT - abstract val environment: ViewEnvironment - abstract val showRendering: ViewShowRendering - - /** [bindShowRendering] has been called, [startShowing] has not. */ - data class New( - override val showing: RenderingT, - override val environment: ViewEnvironment, - override val showRendering: ViewShowRendering, - - val starter: (View) -> Unit = { view -> - @Suppress("DEPRECATION") - view.showRendering(view.getRendering()!!, view.environment!!) - } - ) : WorkflowViewState() - - /** [startShowing] has been called. It's safe to call [showRendering] now. */ - data class Started( - override val showing: RenderingT, - override val environment: ViewEnvironment, - override val showRendering: ViewShowRendering - ) : WorkflowViewState() -} - -@WorkflowUiExperimentalApi -@PublishedApi -internal val View.workflowViewStateOrNull: WorkflowViewState<*>? - get() = getTag(R.id.legacy_workflow_view_state) as? WorkflowViewState<*> - -@WorkflowUiExperimentalApi -internal var View.workflowViewState: WorkflowViewState<*> - get() = workflowViewStateOrNull ?: error( - "Expected $this to have been built by a ViewFactory. " + - "Perhaps the factory did not call View.bindShowRendering." - ) - set(value) = setTag(R.id.legacy_workflow_view_state, value) - -@WorkflowUiExperimentalApi internal val View.workflowViewStateAsNew: New<*> - get() = workflowViewState as? New<*> ?: error( - "Expected $this to be un-started, but View.start() has been called" - ) - -@WorkflowUiExperimentalApi internal val View.workflowViewStateAsStarted: Started<*> - get() = workflowViewState as? Started<*> ?: error( - "Expected $this to have been started, but View.start() has not been called" - ) diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewStub.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewStub.kt index 0114871c5c..66f8b38ee2 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewStub.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewStub.kt @@ -188,21 +188,6 @@ public class WorkflowViewStub @JvmOverloads constructor( } } - @Deprecated( - "Use show()", - ReplaceWith( - "show(asScreen(rendering), viewEnvironment)", - "com.squareup.workflow1.ui.asScreen" - ), - ) - public fun update( - rendering: Any, - viewEnvironment: ViewEnvironment - ): View { - show(asScreen(rendering), viewEnvironment) - return holder!!.view - } - /** * Replaces this view with one that can display [rendering]. If the receiver * has already been replaced, updates the replacement if it [canShowRendering]. diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/AlertOverlayDialogFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/AlertOverlayDialogFactory.kt index 5536683daa..d53d763717 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/AlertOverlayDialogFactory.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/AlertOverlayDialogFactory.kt @@ -72,8 +72,7 @@ public fun AlertDialog.toDialogHolder( return OverlayDialogHolder( initialEnvironment = initialEnvironment, dialog = this, - onUpdateBounds = null, - onBackPressed = null + onUpdateBounds = null ) { rendering, _ -> with(this) { if (rendering.cancelable) { diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/AsDialogHolderWithContent.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/AsDialogHolderWithContent.kt index 5c2ab432c8..74a1fcfb1c 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/AsDialogHolderWithContent.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/AsDialogHolderWithContent.kt @@ -72,8 +72,7 @@ public fun > ComponentDialog.asDialogHolderWith // imitation of that one, and is going to be removed soon. return OverlayDialogHolder( initialEnvironment = environment, - dialog = this, - onBackPressed = null + dialog = this ) { newOverlay, newEnvironment -> contentHolder.show( newOverlay.content, diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/BackButtonScreen.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/BackButtonScreen.kt index 05051f2063..196a164b88 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/BackButtonScreen.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/BackButtonScreen.kt @@ -51,7 +51,4 @@ public class BackButtonScreen( view.setBackHandler(backButtonScreen.onBackPressed) } } - - @Deprecated("Use content", ReplaceWith("content")) - public val wrapped: C = content } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/BackStackContainer.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/BackStackContainer.kt index 3e1c494b4f..1e6bdbb961 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/BackStackContainer.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/BackStackContainer.kt @@ -41,7 +41,8 @@ import com.squareup.workflow1.ui.toViewFactory * This container supports saving and restoring the view state of each of its subviews corresponding * to the renderings in its [BackStackScreen]. It supports two distinct state mechanisms: * 1. Classic view hierarchy state ([View.onSaveInstanceState]/[View.onRestoreInstanceState]) - * 2. AndroidX [SavedStateRegistry] via [SavedStateRegistryOwner]. + * 2. AndroidX [SavedStateRegistry] via + * [SavedStateRegistryOwner][androidx.savedstate.SavedStateRegistryOwner]. */ @WorkflowUiExperimentalApi public open class BackStackContainer @JvmOverloads constructor( diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/DialogSession.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/DialogSession.kt index 464233cb65..38fec6d80c 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/DialogSession.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/DialogSession.kt @@ -79,12 +79,10 @@ internal class DialogSession( /** * Wrap the given dialog holder to maintain [allowEvents] on each update. */ - @Suppress("DEPRECATION") private val holder: OverlayDialogHolder = OverlayDialogHolder( holder.environment, holder.dialog, - holder.onUpdateBounds, - holder.onBackPressed + holder.onUpdateBounds ) { overlay, environment -> allowEvents = !environment[CoveredByModal] holder.show(overlay, environment) @@ -120,20 +118,10 @@ internal class DialogSession( return !allowEvents || realWindowCallback.dispatchTouchEvent(event) } - @Suppress("DEPRECATION") override fun dispatchKeyEvent(event: KeyEvent): Boolean { // Consume all events if we've been told to do so. if (!allowEvents) return true - // If there is an onBackPressed handler invoke it instead of allowing - // the normal machinery to call Dialog.onBackPressed. - if (event.isBackPress) { - holder.onBackPressed?.let { onBackPressed -> - onBackPressed.invoke() - return true - } - } - // Allow the usual handling, including the usual call to Dialog.onBackPressed. return realWindowCallback.dispatchKeyEvent(event) } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/EnvironmentScreenLegacyViewFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/EnvironmentScreenLegacyViewFactory.kt deleted file mode 100644 index 79c9d0ec2c..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/EnvironmentScreenLegacyViewFactory.kt +++ /dev/null @@ -1,18 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.navigation - -import com.squareup.workflow1.ui.DecorativeViewFactory -import com.squareup.workflow1.ui.EnvironmentScreen -import com.squareup.workflow1.ui.ViewFactory -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi - -@Suppress("DEPRECATION") -@WorkflowUiExperimentalApi -internal object EnvironmentScreenLegacyViewFactory : ViewFactory> -by DecorativeViewFactory( - type = EnvironmentScreen::class, - map = { environmentScreen, inheritedEnvironment -> - Pair(environmentScreen.content, environmentScreen.environment + inheritedEnvironment) - } -) diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/OverlayDialogHolder.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/OverlayDialogHolder.kt index 6bea33ce2b..79fdfb77de 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/OverlayDialogHolder.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/OverlayDialogHolder.kt @@ -4,7 +4,6 @@ import android.app.Dialog import android.graphics.Rect import com.squareup.workflow1.ui.ViewEnvironment import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.onBackPressedDispatcherOwnerOrNull import com.squareup.workflow1.ui.compatible import com.squareup.workflow1.ui.show @@ -45,37 +44,17 @@ public interface OverlayDialogHolder { */ public val onUpdateBounds: ((Rect) -> Unit)? - /** - * Optional function to be called when the [dialog] window receives a back button event, - * instead of [Dialog.onBackPressed]. - * - * The default implementation provided by the factory function below looks for the - * [OnBackPressedDispatcherOwner][onBackPressedDispatcherOwnerOrNull] - * and invokes its [onBackPressed][androidx.activity.OnBackPressedDispatcher.onBackPressed] - * method. - */ - @Deprecated("This will soon be deleted. Use ComponentDialog and OnBackPressedDispatcher.") - public val onBackPressed: (() -> Unit)? - public companion object { public operator fun invoke( initialEnvironment: ViewEnvironment, dialog: Dialog, onUpdateBounds: ((Rect) -> Unit)? = { dialog.setBounds(it) }, - onBackPressed: (() -> Unit)? = { - dialog.decorView.onBackPressedDispatcherOwnerOrNull() - ?.onBackPressedDispatcher - ?.let { - if (it.hasEnabledCallbacks()) it.onBackPressed() - } - }, runner: (rendering: OverlayT, environment: ViewEnvironment) -> Unit ): OverlayDialogHolder { return RealOverlayDialogHolder( initialEnvironment, dialog, onUpdateBounds, - onBackPressed, runner ) } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/RealOverlayDialogHolder.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/RealOverlayDialogHolder.kt index 455f02626f..a6d7cbb11b 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/RealOverlayDialogHolder.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/RealOverlayDialogHolder.kt @@ -10,10 +10,6 @@ internal class RealOverlayDialogHolder( initialEnvironment: ViewEnvironment, override val dialog: Dialog, override val onUpdateBounds: ((Rect) -> Unit)?, - @Deprecated( - "This will be deleted in the next release, use ComponentDialog and OnBackPressedDispatcher." - ) - override val onBackPressed: (() -> Unit)?, runnerFunction: (rendering: OverlayT, environment: ViewEnvironment) -> Unit ) : OverlayDialogHolder { diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/ScreenOverlayDialogFactory.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/ScreenOverlayDialogFactory.kt deleted file mode 100644 index 166191f81c..0000000000 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/navigation/ScreenOverlayDialogFactory.kt +++ /dev/null @@ -1,92 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.navigation - -import android.app.Dialog -import android.content.Context -import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL -import com.squareup.workflow1.ui.Screen -import com.squareup.workflow1.ui.ScreenViewHolder -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.onBackPressedDispatcherOwner -import com.squareup.workflow1.ui.androidx.WorkflowLifecycleOwner -import com.squareup.workflow1.ui.show -import com.squareup.workflow1.ui.startShowing -import com.squareup.workflow1.ui.toViewFactory -import kotlin.reflect.KClass - -@Deprecated("Use ComponentDialog.asDialogHolderWithContent") -@WorkflowUiExperimentalApi -public open class ScreenOverlayDialogFactory>( - override val type: KClass -) : OverlayDialogFactory { - /** - * Build the [Dialog], using [content] as its [contentView][Dialog.setContentView]. - * Open to allow customization, typically theming. Subclasses need not call `super`. - * - Note that the default implementation calls the provided [Dialog.setContent] - * extension for typical setup. - * - Be sure to call [ScreenViewHolder.show] from [OverlayDialogHolder.runner]. - */ - public open fun buildDialogWithContent( - initialRendering: O, - initialEnvironment: ViewEnvironment, - content: ScreenViewHolder - ): OverlayDialogHolder { - val dialog = Dialog(content.view.context).apply { setContent(content) } - val modal = initialRendering is ModalOverlay - - return OverlayDialogHolder(initialEnvironment, dialog) { overlayRendering, environment -> - // For a modal, on each update put a no-op backHandler in place on the - // decorView before updating, to ensure that the global androidx - // OnBackPressedDispatcher doesn't fire any set by lower layers. We put this - // in place before each call to show(), so the real content view will be able - // to clobber it. - if (modal) content.view.setBackHandler {} - content.show(overlayRendering.content, environment) - } - } - - /** - * Creates the [ScreenViewHolder] for [initialRendering.content][ScreenOverlay.content] - * and then calls [buildDialogWithContent] to create [Dialog] in an [OverlayDialogHolder]. - */ - final override fun buildDialog( - initialRendering: O, - initialEnvironment: ViewEnvironment, - context: Context - ): OverlayDialogHolder { - val contentViewHolder = initialRendering.content.toViewFactory(initialEnvironment) - .startShowing(initialRendering.content, initialEnvironment, context) { view, doStart -> - // Note that we never call destroyOnDetach for this owner. That's okay because - // DialogSession.showNewDialog puts one in place above us on the decor view, - // and cleans it up. It's in place by the time we attach to the window, and - // so becomes our parent. - WorkflowLifecycleOwner.installOn( - view, - initialEnvironment.onBackPressedDispatcherOwner(view) - ) - doStart() - } - - return buildDialogWithContent( - initialRendering, - initialEnvironment, - contentViewHolder - ).also { holder -> - val window = requireNotNull(holder.dialog.window) { "Dialog must be attached to a window." } - // Note that we always tell Android to make the window non-modal, regardless of our own - // notion of its modality. Even a modal dialog should only block events within - // the appropriate bounds, but Android makes them block everywhere. - window.addFlags(FLAG_NOT_TOUCH_MODAL) - } - } -} - -@Deprecated("Use ComponentDialog.asDialogHolderWithContent.") -@OptIn(WorkflowUiExperimentalApi::class) -public fun Dialog.setContent(contentHolder: ScreenViewHolder<*>) { - setCancelable(false) - setContentView(contentHolder.view) - fixBackgroundAndDimming() -} diff --git a/workflow-ui/core-android/src/main/res/values/ids.xml b/workflow-ui/core-android/src/main/res/values/ids.xml index 590f08f752..49870f9dc1 100644 --- a/workflow-ui/core-android/src/main/res/values/ids.xml +++ b/workflow-ui/core-android/src/main/res/values/ids.xml @@ -5,8 +5,6 @@ Otherwise animates its entire body. --> - - diff --git a/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/LegacyAndroidViewRegistryTest.kt b/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/LegacyAndroidViewRegistryTest.kt deleted file mode 100644 index 30e6c18b48..0000000000 --- a/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/LegacyAndroidViewRegistryTest.kt +++ /dev/null @@ -1,210 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import com.google.common.truth.Truth.assertThat -import com.squareup.workflow1.ui.ViewRegistry.Entry -import com.squareup.workflow1.ui.navigation.mockView -import org.junit.Test -import org.mockito.kotlin.mock -import kotlin.reflect.KClass -import kotlin.test.assertFailsWith -import kotlin.test.assertTrue - -@OptIn(WorkflowUiExperimentalApi::class) -internal class LegacyAndroidViewRegistryTest { - - @OptIn(WorkflowUiExperimentalApi::class) - @Test - fun missingBindingMessage_isUseful() { - val emptyReg = object : ViewRegistry { - override val keys: Set> = emptySet() - override fun getEntryFor( - renderingType: KClass - ): Entry? = null - } - - val error = assertFailsWith { - emptyReg.buildView("render this, bud") - } - assertThat(error.message).isEqualTo( - "A ViewFactory should have been registered to display " + - "render this, bud, or that class should implement AndroidViewRendering." - ) - } - - @Test fun `getFactoryFor delegates to composite registries`() { - val fooFactory = TestViewFactory(FooRendering::class) - val barFactory = TestViewFactory(BarRendering::class) - val bazFactory = TestViewFactory(BazRendering::class) - val fooBarRegistry = TestRegistry( - mapOf( - FooRendering::class to fooFactory, - BarRendering::class to barFactory - ) - ) - val bazRegistry = TestRegistry(factories = mapOf(BazRendering::class to bazFactory)) - val registry = fooBarRegistry + bazRegistry - - assertThat(registry.getEntryFor(FooRendering::class)) - .isSameInstanceAs(fooFactory) - assertThat(registry.getEntryFor(BarRendering::class)) - .isSameInstanceAs(barFactory) - assertThat(registry.getEntryFor(BazRendering::class)) - .isSameInstanceAs(bazFactory) - } - - @Test fun `getFactoryFor returns null on missing registry in composite`() { - val fooRegistry = TestRegistry(setOf(FooRendering::class)) - val bazRegistry = TestRegistry(setOf(BazRendering::class)) - val registry = bazRegistry + fooRegistry - - assertThat(registry.getEntryFor(BarRendering::class)).isNull() - } - - @Test fun `keys includes all composite registries' keys`() { - val fooBarRegistry = TestRegistry(setOf(FooRendering::class, BarRendering::class)) - val bazRegistry = TestRegistry(setOf(BazRendering::class)) - val registry = fooBarRegistry + bazRegistry - - assertThat(registry.keys).containsExactly( - FooRendering::class, - BarRendering::class, - BazRendering::class - ) - } - - @Test fun `keys from bindings`() { - val factory1 = TestViewFactory(FooRendering::class) - val factory2 = TestViewFactory(BarRendering::class) - val registry = ViewRegistry(factory1, factory2) - - assertThat(registry.keys).containsExactly(factory1.type, factory2.type) - } - - @Test fun `constructor throws on duplicates`() { - val factory1 = TestViewFactory(FooRendering::class) - val factory2 = TestViewFactory(FooRendering::class) - - val error = assertFailsWith { - ViewRegistry(factory1, factory2) - } - assertThat(error).hasMessageThat() - .endsWith("must not have duplicate entries.") - assertThat(error).hasMessageThat() - .contains(FooRendering::class.java.name) - } - - @Test fun `getFactoryFor works`() { - val fooFactory = TestViewFactory(FooRendering::class) - val registry = ViewRegistry(fooFactory) - - val factory = registry.getEntryFor(FooRendering::class) - assertThat(factory).isSameInstanceAs(fooFactory) - } - - @Test fun `getFactoryFor returns null on missing binding`() { - val fooFactory = TestViewFactory(FooRendering::class) - val registry = ViewRegistry(fooFactory) - - assertThat(registry.getEntryFor(BarRendering::class)).isNull() - } - - @Test fun `buildView honors AndroidViewRendering`() { - val registry = ViewRegistry() - registry.buildView(ViewRendering) - assertThat(ViewRendering.viewFactory.called).isTrue() - } - - @Test fun `buildView prefers registry entries to AndroidViewRendering`() { - val registry = ViewRegistry(overrideViewRenderingFactory) - registry.buildView(ViewRendering) - assertThat(ViewRendering.viewFactory.called).isFalse() - assertThat(overrideViewRenderingFactory.called).isTrue() - } - - @Test fun `buildView auto converts unwrapped Screen and updates screenOrNull correctly`() { - val registry = ViewRegistry() - val view = registry.buildView(ScreenRendering) - view.start() - assertThat(view.getRendering()).isSameInstanceAs(ScreenRendering) - assertThat(view.screenOrNull).isSameInstanceAs(ScreenRendering) - } - - @Test fun `buildView auto converts wrapped Screen and updates screen correctly`() { - val registry = ViewRegistry() - val rendering = Named(ScreenRendering, "fnord") - val view = registry.buildView(rendering) - view.start() - assertThat(compatible(view.getRendering()!!, rendering)).isTrue() - assertThat(compatible(view.screen, asScreen(rendering))).isTrue() - } - - @Test fun `ViewRegistry with no arguments infers type`() { - val registry = ViewRegistry() - assertTrue(registry.keys.isEmpty()) - } - - private object FooRendering - private object BarRendering - private object BazRendering - - private object ViewRendering : AndroidViewRendering { - override val viewFactory = TestViewFactory(ViewRendering::class) - } - - private object ScreenRendering : AndroidScreen { - override val viewFactory = TestScreenViewFactory(ScreenRendering::class) - } - - private val overrideViewRenderingFactory = TestViewFactory(ViewRendering::class) - - private class TestRegistry(private val factories: Map, ViewFactory<*>>) : ViewRegistry { - constructor(keys: Set>) : this(keys.associateWith { TestViewFactory(it) }) - - override val keys: Set> get() = factories.keys - - @Suppress("UNCHECKED_CAST") - override fun getEntryFor( - renderingType: KClass - ): Entry = factories.getValue(renderingType) as Entry - } - - @OptIn(WorkflowUiExperimentalApi::class) - private fun ViewRegistry.buildView(rendering: R): View = - buildView(rendering, ViewEnvironment.EMPTY + this, mock()) - - @OptIn(WorkflowUiExperimentalApi::class) - private class TestViewFactory(override val type: KClass) : ViewFactory { - var called = false - - override fun buildView( - initialRendering: R, - initialViewEnvironment: ViewEnvironment, - contextForNewView: Context, - container: ViewGroup? - ): View { - called = true - return mockView().also { - it.bindShowRendering(initialRendering, initialViewEnvironment) { _, _ -> } - } - } - } - - @OptIn(WorkflowUiExperimentalApi::class) - private class TestScreenViewFactory( - override val type: KClass - ) : ScreenViewFactory { - override fun buildView( - initialRendering: R, - initialEnvironment: ViewEnvironment, - context: Context, - container: ViewGroup? - ): ScreenViewHolder { - return ScreenViewHolder(initialEnvironment, mockView()) { _, _ -> } - } - } -} diff --git a/workflow-ui/core-common/api/core-common.api b/workflow-ui/core-common/api/core-common.api index a2506476ac..4fff428711 100644 --- a/workflow-ui/core-common/api/core-common.api +++ b/workflow-ui/core-common/api/core-common.api @@ -1,18 +1,3 @@ -public final class com/squareup/workflow1/ui/AsScreen : com/squareup/workflow1/ui/Screen, com/squareup/workflow1/ui/Wrapper { - public fun (Ljava/lang/Object;)V - public fun asSequence ()Lkotlin/sequences/Sequence; - public fun getCompatibilityKey ()Ljava/lang/String; - public fun getContent ()Ljava/lang/Object; - public final fun getRendering ()Ljava/lang/Object; - public fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/AsScreen; - public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Container; - public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Wrapper; -} - -public final class com/squareup/workflow1/ui/AsScreenKt { - public static final fun asScreen (Ljava/lang/Object;)Lcom/squareup/workflow1/ui/Screen; -} - public abstract interface class com/squareup/workflow1/ui/Compatible { public static final field Companion Lcom/squareup/workflow1/ui/Compatible$Companion; public abstract fun getCompatibilityKey ()Ljava/lang/String; @@ -32,40 +17,6 @@ public abstract interface class com/squareup/workflow1/ui/Container { public abstract fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Container; } -public final class com/squareup/workflow1/ui/EnvironmentScreen : com/squareup/workflow1/ui/Screen, com/squareup/workflow1/ui/Wrapper { - public fun (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;)V - public synthetic fun (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun asSequence ()Lkotlin/sequences/Sequence; - public fun getCompatibilityKey ()Ljava/lang/String; - public fun getContent ()Lcom/squareup/workflow1/ui/Screen; - public synthetic fun getContent ()Ljava/lang/Object; - public final fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment; - public final fun getWrapped ()Lcom/squareup/workflow1/ui/Screen; - public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Container; - public fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/EnvironmentScreen; - public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Wrapper; -} - -public final class com/squareup/workflow1/ui/EnvironmentScreenKt { - public static final fun withEnvironment (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/EnvironmentScreen; - public static synthetic fun withEnvironment$default (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/EnvironmentScreen; - public static final fun withRegistry (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewRegistry;)Lcom/squareup/workflow1/ui/EnvironmentScreen; -} - -public final class com/squareup/workflow1/ui/Named : com/squareup/workflow1/ui/Compatible { - public fun (Ljava/lang/Object;Ljava/lang/String;)V - public final fun component1 ()Ljava/lang/Object; - public final fun component2 ()Ljava/lang/String; - public final fun copy (Ljava/lang/Object;Ljava/lang/String;)Lcom/squareup/workflow1/ui/Named; - public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/Named;Ljava/lang/Object;Ljava/lang/String;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/Named; - public fun equals (Ljava/lang/Object;)Z - public fun getCompatibilityKey ()Ljava/lang/String; - public final fun getName ()Ljava/lang/String; - public final fun getWrapped ()Ljava/lang/Object; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - public final class com/squareup/workflow1/ui/NamedScreen : com/squareup/workflow1/ui/Screen, com/squareup/workflow1/ui/Wrapper { public fun (Lcom/squareup/workflow1/ui/Screen;Ljava/lang/String;)V public fun asSequence ()Lkotlin/sequences/Sequence; @@ -78,7 +29,6 @@ public final class com/squareup/workflow1/ui/NamedScreen : com/squareup/workflow public fun getContent ()Lcom/squareup/workflow1/ui/Screen; public synthetic fun getContent ()Ljava/lang/Object; public final fun getName ()Ljava/lang/String; - public final fun getWrapped ()Lcom/squareup/workflow1/ui/Screen; public fun hashCode ()I public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Container; public fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/NamedScreen; @@ -102,9 +52,6 @@ public final class com/squareup/workflow1/ui/TextControllerKt { public final class com/squareup/workflow1/ui/ViewEnvironment { public static final field Companion Lcom/squareup/workflow1/ui/ViewEnvironment$Companion; - public fun ()V - public fun (Ljava/util/Map;)V - public synthetic fun (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z public final fun get (Lcom/squareup/workflow1/ui/ViewEnvironmentKey;)Ljava/lang/Object; public final fun getMap ()Ljava/util/Map; @@ -120,7 +67,6 @@ public final class com/squareup/workflow1/ui/ViewEnvironment$Companion { public abstract class com/squareup/workflow1/ui/ViewEnvironmentKey { public fun ()V - public fun (Lkotlin/reflect/KClass;)V public fun combine (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; public final fun equals (Ljava/lang/Object;)Z public abstract fun getDefault ()Ljava/lang/Object; @@ -168,7 +114,7 @@ public final class com/squareup/workflow1/ui/Wrapper$DefaultImpls { public static fun getCompatibilityKey (Lcom/squareup/workflow1/ui/Wrapper;)Ljava/lang/String; } -public final class com/squareup/workflow1/ui/navigation/AlertOverlay : com/squareup/workflow1/ui/navigation/ModalOverlay { +public final class com/squareup/workflow1/ui/container/AlertOverlay : com/squareup/workflow1/ui/container/ModalOverlay { public fun (Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)V public synthetic fun (Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/util/Map; @@ -176,8 +122,8 @@ public final class com/squareup/workflow1/ui/navigation/AlertOverlay : com/squar public final fun component3 ()Ljava/lang/String; public final fun component4 ()Z public final fun component5 ()Lkotlin/jvm/functions/Function1; - public final fun copy (Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/AlertOverlay; - public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/navigation/AlertOverlay;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/navigation/AlertOverlay; + public final fun copy (Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/AlertOverlay; + public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/container/AlertOverlay;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/container/AlertOverlay; public fun equals (Ljava/lang/Object;)Z public final fun getButtons ()Ljava/util/Map; public final fun getCancelable ()Z @@ -188,55 +134,54 @@ public final class com/squareup/workflow1/ui/navigation/AlertOverlay : com/squar public fun toString ()Ljava/lang/String; } -public final class com/squareup/workflow1/ui/navigation/AlertOverlay$Button : java/lang/Enum { - public static final field NEGATIVE Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Button; - public static final field NEUTRAL Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Button; - public static final field POSITIVE Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Button; +public final class com/squareup/workflow1/ui/container/AlertOverlay$Button : java/lang/Enum { + public static final field NEGATIVE Lcom/squareup/workflow1/ui/container/AlertOverlay$Button; + public static final field NEUTRAL Lcom/squareup/workflow1/ui/container/AlertOverlay$Button; + public static final field POSITIVE Lcom/squareup/workflow1/ui/container/AlertOverlay$Button; public static fun getEntries ()Lkotlin/enums/EnumEntries; - public static fun valueOf (Ljava/lang/String;)Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Button; - public static fun values ()[Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Button; + public static fun valueOf (Ljava/lang/String;)Lcom/squareup/workflow1/ui/container/AlertOverlay$Button; + public static fun values ()[Lcom/squareup/workflow1/ui/container/AlertOverlay$Button; } -public abstract class com/squareup/workflow1/ui/navigation/AlertOverlay$Event { +public abstract class com/squareup/workflow1/ui/container/AlertOverlay$Event { } -public final class com/squareup/workflow1/ui/navigation/AlertOverlay$Event$ButtonClicked : com/squareup/workflow1/ui/navigation/AlertOverlay$Event { - public fun (Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Button;)V - public final fun component1 ()Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Button; - public final fun copy (Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Button;)Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Event$ButtonClicked; - public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Event$ButtonClicked;Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Button;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Event$ButtonClicked; +public final class com/squareup/workflow1/ui/container/AlertOverlay$Event$ButtonClicked : com/squareup/workflow1/ui/container/AlertOverlay$Event { + public fun (Lcom/squareup/workflow1/ui/container/AlertOverlay$Button;)V + public final fun component1 ()Lcom/squareup/workflow1/ui/container/AlertOverlay$Button; + public final fun copy (Lcom/squareup/workflow1/ui/container/AlertOverlay$Button;)Lcom/squareup/workflow1/ui/container/AlertOverlay$Event$ButtonClicked; + public static synthetic fun copy$default (Lcom/squareup/workflow1/ui/container/AlertOverlay$Event$ButtonClicked;Lcom/squareup/workflow1/ui/container/AlertOverlay$Button;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/container/AlertOverlay$Event$ButtonClicked; public fun equals (Ljava/lang/Object;)Z - public final fun getButton ()Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Button; + public final fun getButton ()Lcom/squareup/workflow1/ui/container/AlertOverlay$Button; public fun hashCode ()I public fun toString ()Ljava/lang/String; } -public final class com/squareup/workflow1/ui/navigation/AlertOverlay$Event$Canceled : com/squareup/workflow1/ui/navigation/AlertOverlay$Event { - public static final field INSTANCE Lcom/squareup/workflow1/ui/navigation/AlertOverlay$Event$Canceled; +public final class com/squareup/workflow1/ui/container/AlertOverlay$Event$Canceled : com/squareup/workflow1/ui/container/AlertOverlay$Event { + public static final field INSTANCE Lcom/squareup/workflow1/ui/container/AlertOverlay$Event$Canceled; } -public final class com/squareup/workflow1/ui/navigation/BackStackConfig : java/lang/Enum { - public static final field Companion Lcom/squareup/workflow1/ui/navigation/BackStackConfig$Companion; - public static final field First Lcom/squareup/workflow1/ui/navigation/BackStackConfig; - public static final field None Lcom/squareup/workflow1/ui/navigation/BackStackConfig; - public static final field Other Lcom/squareup/workflow1/ui/navigation/BackStackConfig; +public final class com/squareup/workflow1/ui/container/BackStackConfig : java/lang/Enum { + public static final field Companion Lcom/squareup/workflow1/ui/container/BackStackConfig$Companion; + public static final field First Lcom/squareup/workflow1/ui/container/BackStackConfig; + public static final field None Lcom/squareup/workflow1/ui/container/BackStackConfig; + public static final field Other Lcom/squareup/workflow1/ui/container/BackStackConfig; public static fun getEntries ()Lkotlin/enums/EnumEntries; - public static fun valueOf (Ljava/lang/String;)Lcom/squareup/workflow1/ui/navigation/BackStackConfig; - public static fun values ()[Lcom/squareup/workflow1/ui/navigation/BackStackConfig; + public static fun valueOf (Ljava/lang/String;)Lcom/squareup/workflow1/ui/container/BackStackConfig; + public static fun values ()[Lcom/squareup/workflow1/ui/container/BackStackConfig; } -public final class com/squareup/workflow1/ui/navigation/BackStackConfig$Companion : com/squareup/workflow1/ui/ViewEnvironmentKey { - public fun getDefault ()Lcom/squareup/workflow1/ui/navigation/BackStackConfig; +public final class com/squareup/workflow1/ui/container/BackStackConfig$Companion : com/squareup/workflow1/ui/ViewEnvironmentKey { + public fun getDefault ()Lcom/squareup/workflow1/ui/container/BackStackConfig; public synthetic fun getDefault ()Ljava/lang/Object; } -public final class com/squareup/workflow1/ui/navigation/BackStackConfigKt { - public static final fun plus (Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/navigation/BackStackConfig;)Lcom/squareup/workflow1/ui/ViewEnvironment; +public final class com/squareup/workflow1/ui/container/BackStackConfigKt { + public static final fun plus (Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/container/BackStackConfig;)Lcom/squareup/workflow1/ui/ViewEnvironment; } -public final class com/squareup/workflow1/ui/navigation/BackStackScreen : com/squareup/workflow1/ui/Container, com/squareup/workflow1/ui/Screen { - public static final field Companion Lcom/squareup/workflow1/ui/navigation/BackStackScreen$Companion; - public fun (Lcom/squareup/workflow1/ui/Screen;Ljava/util/List;)V +public final class com/squareup/workflow1/ui/container/BackStackScreen : com/squareup/workflow1/ui/Container, com/squareup/workflow1/ui/Screen { + public static final field Companion Lcom/squareup/workflow1/ui/container/BackStackScreen$Companion; public fun (Lcom/squareup/workflow1/ui/Screen;[Lcom/squareup/workflow1/ui/Screen;)V public fun asSequence ()Lkotlin/sequences/Sequence; public fun equals (Ljava/lang/Object;)Z @@ -246,36 +191,53 @@ public final class com/squareup/workflow1/ui/navigation/BackStackScreen : com/sq public final fun getTop ()Lcom/squareup/workflow1/ui/Screen; public fun hashCode ()I public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Container; - public fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/BackStackScreen; - public final fun mapIndexed (Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/navigation/BackStackScreen; + public fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/BackStackScreen; + public final fun mapIndexed (Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow1/ui/container/BackStackScreen; public fun toString ()Ljava/lang/String; } -public final class com/squareup/workflow1/ui/navigation/BackStackScreen$Companion { - public final fun fromList (Ljava/util/List;)Lcom/squareup/workflow1/ui/navigation/BackStackScreen; - public final fun fromListOrNull (Ljava/util/List;)Lcom/squareup/workflow1/ui/navigation/BackStackScreen; +public final class com/squareup/workflow1/ui/container/BackStackScreen$Companion { + public final fun fromList (Ljava/util/List;)Lcom/squareup/workflow1/ui/container/BackStackScreen; + public final fun fromListOrNull (Ljava/util/List;)Lcom/squareup/workflow1/ui/container/BackStackScreen; } -public final class com/squareup/workflow1/ui/navigation/BackStackScreenKt { - public static final fun plus (Lcom/squareup/workflow1/ui/navigation/BackStackScreen;Lcom/squareup/workflow1/ui/navigation/BackStackScreen;)Lcom/squareup/workflow1/ui/navigation/BackStackScreen; - public static final fun toBackStackScreen (Ljava/util/List;)Lcom/squareup/workflow1/ui/navigation/BackStackScreen; - public static final fun toBackStackScreenOrNull (Ljava/util/List;)Lcom/squareup/workflow1/ui/navigation/BackStackScreen; +public final class com/squareup/workflow1/ui/container/BackStackScreenKt { + public static final fun plus (Lcom/squareup/workflow1/ui/container/BackStackScreen;Lcom/squareup/workflow1/ui/container/BackStackScreen;)Lcom/squareup/workflow1/ui/container/BackStackScreen; + public static final fun toBackStackScreen (Ljava/util/List;)Lcom/squareup/workflow1/ui/container/BackStackScreen; + public static final fun toBackStackScreenOrNull (Ljava/util/List;)Lcom/squareup/workflow1/ui/container/BackStackScreen; } -public final class com/squareup/workflow1/ui/navigation/BodyAndOverlaysScreen : com/squareup/workflow1/ui/Compatible, com/squareup/workflow1/ui/Screen { +public final class com/squareup/workflow1/ui/container/BodyAndOverlaysScreen : com/squareup/workflow1/ui/Compatible, com/squareup/workflow1/ui/Screen { public fun (Lcom/squareup/workflow1/ui/Screen;Ljava/util/List;Ljava/lang/String;)V public synthetic fun (Lcom/squareup/workflow1/ui/Screen;Ljava/util/List;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lcom/squareup/workflow1/ui/Screen;[Lcom/squareup/workflow1/ui/navigation/Overlay;)V public final fun getBody ()Lcom/squareup/workflow1/ui/Screen; public fun getCompatibilityKey ()Ljava/lang/String; public final fun getName ()Ljava/lang/String; public final fun getOverlays ()Ljava/util/List; - public final fun mapBody (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/BodyAndOverlaysScreen; - public final fun mapModals (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/BodyAndOverlaysScreen; - public final fun mapOverlays (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/BodyAndOverlaysScreen; + public final fun mapBody (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/BodyAndOverlaysScreen; + public final fun mapOverlays (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/BodyAndOverlaysScreen; +} + +public final class com/squareup/workflow1/ui/container/EnvironmentScreen : com/squareup/workflow1/ui/Screen, com/squareup/workflow1/ui/Wrapper { + public fun (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;)V + public synthetic fun (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun asSequence ()Lkotlin/sequences/Sequence; + public fun getCompatibilityKey ()Ljava/lang/String; + public fun getContent ()Lcom/squareup/workflow1/ui/Screen; + public synthetic fun getContent ()Ljava/lang/Object; + public final fun getEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment; + public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Container; + public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Wrapper; + public fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/EnvironmentScreen; +} + +public final class com/squareup/workflow1/ui/container/EnvironmentScreenKt { + public static final fun withEnvironment (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/container/EnvironmentScreen; + public static synthetic fun withEnvironment$default (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/container/EnvironmentScreen; + public static final fun withRegistry (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewRegistry;)Lcom/squareup/workflow1/ui/container/EnvironmentScreen; } -public final class com/squareup/workflow1/ui/navigation/FullScreenModal : com/squareup/workflow1/ui/navigation/ModalOverlay, com/squareup/workflow1/ui/navigation/ScreenOverlay { +public final class com/squareup/workflow1/ui/container/FullScreenModal : com/squareup/workflow1/ui/container/ModalOverlay, com/squareup/workflow1/ui/container/ScreenOverlay { public fun (Lcom/squareup/workflow1/ui/Screen;)V public fun asSequence ()Lkotlin/sequences/Sequence; public fun getCompatibilityKey ()Ljava/lang/String; @@ -283,23 +245,23 @@ public final class com/squareup/workflow1/ui/navigation/FullScreenModal : com/sq public synthetic fun getContent ()Ljava/lang/Object; public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Container; public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Wrapper; - public fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/FullScreenModal; - public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/ScreenOverlay; + public fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/FullScreenModal; + public synthetic fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/ScreenOverlay; } -public abstract interface class com/squareup/workflow1/ui/navigation/ModalOverlay : com/squareup/workflow1/ui/navigation/Overlay { +public abstract interface class com/squareup/workflow1/ui/container/ModalOverlay : com/squareup/workflow1/ui/container/Overlay { } -public abstract interface class com/squareup/workflow1/ui/navigation/Overlay { +public abstract interface class com/squareup/workflow1/ui/container/Overlay { } -public abstract interface class com/squareup/workflow1/ui/navigation/ScreenOverlay : com/squareup/workflow1/ui/Wrapper, com/squareup/workflow1/ui/navigation/Overlay { +public abstract interface class com/squareup/workflow1/ui/container/ScreenOverlay : com/squareup/workflow1/ui/Wrapper, com/squareup/workflow1/ui/container/Overlay { public abstract fun getContent ()Lcom/squareup/workflow1/ui/Screen; - public abstract fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/ScreenOverlay; + public abstract fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/container/ScreenOverlay; } -public final class com/squareup/workflow1/ui/navigation/ScreenOverlay$DefaultImpls { - public static fun asSequence (Lcom/squareup/workflow1/ui/navigation/ScreenOverlay;)Lkotlin/sequences/Sequence; - public static fun getCompatibilityKey (Lcom/squareup/workflow1/ui/navigation/ScreenOverlay;)Ljava/lang/String; +public final class com/squareup/workflow1/ui/container/ScreenOverlay$DefaultImpls { + public static fun asSequence (Lcom/squareup/workflow1/ui/container/ScreenOverlay;)Lkotlin/sequences/Sequence; + public static fun getCompatibilityKey (Lcom/squareup/workflow1/ui/container/ScreenOverlay;)Ljava/lang/String; } diff --git a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/AsScreen.kt b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/AsScreen.kt deleted file mode 100644 index 72926206b7..0000000000 --- a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/AsScreen.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.squareup.workflow1.ui - -/** - * Provides backward compatibility for legacy non-[Screen] renderings. - * This is a migration tool for code bases that are still adopting the `Screen` and - * `Overlay` interfaces, and will be deprecated and deleted sooner or later. - */ -@WorkflowUiExperimentalApi -public class AsScreen( - override val content: C -) : Screen, Wrapper { - init { - check(content !is Screen) { - "AsScreen is for converting non-Screen renderings, it should not wrap Screen $content." - } - } - - @Deprecated("Use content", ReplaceWith("content")) - public val rendering: C = content - - override fun map(transform: (C) -> D): AsScreen = - AsScreen(transform(content)) -} - -/** - * Ensures [rendering] implements [Screen], wrapping it in an [AsScreen] if necessary. - * - * This is a migration tool for code bases that are still adopting the `Screen` and - * `Overlay` interfaces, and will be deprecated and deleted sooner or later. - */ -@WorkflowUiExperimentalApi -public fun asScreen(rendering: Any): Screen { - return when (rendering) { - is Screen -> rendering - else -> AsScreen(rendering) - } -} diff --git a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/EnvironmentScreen.kt b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/EnvironmentScreen.kt index 1487c67f7c..bf3137101f 100644 --- a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/EnvironmentScreen.kt +++ b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/EnvironmentScreen.kt @@ -15,17 +15,15 @@ public class EnvironmentScreen( ) : Wrapper, Screen { override fun map(transform: (C) -> D): EnvironmentScreen = EnvironmentScreen(transform(content), environment) - - @Deprecated("Use content", ReplaceWith("content")) - public val wrapped: C = content } /** * Returns an [EnvironmentScreen] derived from the receiver, whose * [EnvironmentScreen.environment] includes [viewRegistry]. * - * If the receiver is an [EnvironmentScreen], uses [ViewRegistry.merge] - * to preserve the [ViewRegistry] entries of both. + * If the receiver is an [EnvironmentScreen], uses + * [ViewRegistry.merge][com.squareup.workflow1.ui.merge] to preserve the [ViewRegistry] + * entries of both. */ @WorkflowUiExperimentalApi public fun Screen.withRegistry(viewRegistry: ViewRegistry): EnvironmentScreen<*> { @@ -36,8 +34,9 @@ public fun Screen.withRegistry(viewRegistry: ViewRegistry): EnvironmentScreen<*> * Returns an [EnvironmentScreen] derived from the receiver, * whose [EnvironmentScreen.environment] includes the values in the given [environment]. * - * If the receiver is an [EnvironmentScreen], uses [ViewEnvironment.merge] - * to preserve the [ViewRegistry] entries of both. + * If the receiver is an [EnvironmentScreen], uses + * [ViewRegistry.merge][com.squareup.workflow1.ui.merge] to preserve the [ViewRegistry] + * entries of both. */ @WorkflowUiExperimentalApi public fun Screen.withEnvironment( diff --git a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/Named.kt b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/Named.kt deleted file mode 100644 index 289200655a..0000000000 --- a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/Named.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.squareup.workflow1.ui - -/** - * Allows renderings that do not implement [Compatible] themselves to be distinguished - * by more than just their type. Instances are [compatible] if they have the same name - * and have [compatible] [wrapped] fields. - */ -@WorkflowUiExperimentalApi -@Deprecated("Use NamedScreen") -public data class Named( - val wrapped: W, - val name: String -) : Compatible { - init { - require(name.isNotBlank()) { "name must not be blank." } - } - - override val compatibilityKey: String = Compatible.keyFor(wrapped, "Named:$name") - - override fun toString(): String { - return "${super.toString()}: $compatibilityKey" - } -} diff --git a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/NamedScreen.kt b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/NamedScreen.kt index a20196b953..44ee595fc0 100644 --- a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/NamedScreen.kt +++ b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/NamedScreen.kt @@ -18,9 +18,6 @@ public data class NamedScreen( override val compatibilityKey: String = Compatible.keyFor(content, "NamedScreen:$name") - @Deprecated("Use content", ReplaceWith("content")) - public val wrapped: C = content - override fun map(transform: (C) -> D): NamedScreen = NamedScreen(transform(content), name) diff --git a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/ViewEnvironment.kt b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/ViewEnvironment.kt index 9c37d796a4..e7f8917135 100644 --- a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/ViewEnvironment.kt +++ b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/ViewEnvironment.kt @@ -1,7 +1,5 @@ package com.squareup.workflow1.ui -import kotlin.reflect.KClass - /** * Immutable map of values that a parent view can pass down to * its children. Allows containers to give descendants information about @@ -12,11 +10,7 @@ import kotlin.reflect.KClass */ @WorkflowUiExperimentalApi public class ViewEnvironment -@Deprecated( - "To eliminate runtime errors this constructor will become private. " + - "Use ViewEnvironment.EMPTY and ViewEnvironment.plus" -) -constructor( +private constructor( public val map: Map, Any> = emptyMap() ) { public operator fun get(key: ViewEnvironmentKey): T = getOrNull(key) ?: key.default @@ -26,11 +20,9 @@ constructor( val newPair = getOrNull(newKey) ?.let { oldValue -> newKey to newKey.combine(oldValue, newValue) } ?: pair - @Suppress("DEPRECATION") return ViewEnvironment(map + newPair) } - @Suppress("DEPRECATION") public operator fun plus(other: ViewEnvironment): ViewEnvironment { if (this == other) return this if (other.map.isEmpty()) return this @@ -56,7 +48,6 @@ constructor( private fun getOrNull(key: ViewEnvironmentKey): T? = map[key] as? T public companion object { - @Suppress("DEPRECATION") public val EMPTY: ViewEnvironment = ViewEnvironment() } } @@ -71,10 +62,7 @@ constructor( * for an example. */ @WorkflowUiExperimentalApi -public abstract class ViewEnvironmentKey() { - @Deprecated("Use no args constructor", ReplaceWith("ViewEnvironmentKey()")) - public constructor(@Suppress("UNUSED_PARAMETER") type: KClass) : this() - +public abstract class ViewEnvironmentKey { /** * Defines the default value for this key. It is a grievous error for this value to be * dynamic in any way. diff --git a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/navigation/BackStackScreen.kt b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/navigation/BackStackScreen.kt index 61b4e73417..6b3d4c14c8 100644 --- a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/navigation/BackStackScreen.kt +++ b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/navigation/BackStackScreen.kt @@ -31,15 +31,6 @@ public class BackStackScreen internal constructor( vararg rest: StackedT ) : this(listOf(bottom) + rest) - @Deprecated( - "Use fromList", - ReplaceWith("BackStackScreen.fromList(listOf(bottom) + rest)") - ) - public constructor( - bottom: StackedT, - rest: List - ) : this(listOf(bottom) + rest) - override fun asSequence(): Sequence = frames.asSequence() /** diff --git a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/navigation/BodyAndOverlaysScreen.kt b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/navigation/BodyAndOverlaysScreen.kt index f0e0f532e3..db22e4af36 100644 --- a/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/navigation/BodyAndOverlaysScreen.kt +++ b/workflow-ui/core-common/src/main/java/com/squareup/workflow1/ui/navigation/BodyAndOverlaysScreen.kt @@ -80,15 +80,6 @@ public class BodyAndOverlaysScreen( ) : Screen, Compatible { override val compatibilityKey: String = keyFor(this, name) - @Deprecated( - "Use list-based constructor", - ReplaceWith("BodyAndOverlaysScreen(body, listOf(*modals))") - ) - public constructor( - body: B, - vararg modals: O - ) : this(body, modals.toList()) - public fun mapBody(transform: (B) -> S): BodyAndOverlaysScreen { return BodyAndOverlaysScreen(transform(body), overlays, name) } @@ -96,9 +87,4 @@ public class BodyAndOverlaysScreen( public fun mapOverlays(transform: (O) -> N): BodyAndOverlaysScreen { return BodyAndOverlaysScreen(body, overlays.map(transform), name) } - - @Deprecated("Use mapOverlays", ReplaceWith("this.mapOverlays")) - public fun mapModals(transform: (O) -> N): BodyAndOverlaysScreen { - return mapOverlays(transform) - } } diff --git a/workflow-ui/core-common/src/test/java/com/squareup/workflow1/ui/NamedTest.kt b/workflow-ui/core-common/src/test/java/com/squareup/workflow1/ui/NamedTest.kt deleted file mode 100644 index 01caafc88a..0000000000 --- a/workflow-ui/core-common/src/test/java/com/squareup/workflow1/ui/NamedTest.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.squareup.workflow1.ui - -import com.google.common.truth.Truth.assertThat -import org.junit.Test - -// If you try to replace isTrue() with isTrue compilation fails. -@OptIn(WorkflowUiExperimentalApi::class) -@Suppress("DEPRECATION") -internal class NamedTest { - object Whut - object Hey - - @Test fun `same type same name matches`() { - assertThat(compatible(Named(Hey, "eh"), Named(Hey, "eh"))).isTrue() - } - - @Test fun `same type diff name matches`() { - assertThat(compatible(Named(Hey, "blam"), Named(Hey, "bloom"))).isFalse() - } - - @Test fun `diff type same name no match`() { - assertThat(compatible(Named(Hey, "a"), Named(Whut, "a"))).isFalse() - } - - @Test fun recursion() { - assertThat( - compatible( - Named(Named(Hey, "one"), "ho"), - Named(Named(Hey, "one"), "ho") - ) - ).isTrue() - - assertThat( - compatible( - Named(Named(Hey, "one"), "ho"), - Named(Named(Hey, "two"), "ho") - ) - ).isFalse() - - assertThat( - compatible( - Named(Named(Hey, "a"), "ho"), - Named(Named(Whut, "a"), "ho") - ) - ).isFalse() - } - - @Test fun `key recursion`() { - assertThat(Named(Named(Hey, "one"), "ho").compatibilityKey) - .isEqualTo(Named(Named(Hey, "one"), "ho").compatibilityKey) - - assertThat(Named(Named(Hey, "one"), "ho").compatibilityKey) - .isNotEqualTo(Named(Named(Hey, "two"), "ho").compatibilityKey) - - assertThat(Named(Named(Hey, "a"), "ho").compatibilityKey) - .isNotEqualTo(Named(Named(Whut, "a"), "ho").compatibilityKey) - } - - @Test fun `recursive keys are legible`() { - assertThat(Named(Named(Hey, "one"), "ho").compatibilityKey) - .isEqualTo("Named:ho(Named:one(com.squareup.workflow1.ui.NamedTest\$Hey))") - } - - private class Foo(override val compatibilityKey: String) : Compatible - - @Test fun `the test Compatible class actually works`() { - assertThat(compatible(Foo("bar"), Foo("bar"))).isTrue() - assertThat(compatible(Foo("bar"), Foo("baz"))).isFalse() - } - - @Test fun `wrapping custom Compatible compatibility works`() { - assertThat(compatible(Named(Foo("bar"), "name"), Named(Foo("bar"), "name"))).isTrue() - assertThat(compatible(Named(Foo("bar"), "name"), Named(Foo("baz"), "name"))).isFalse() - } - - @Test fun `wrapping custom Compatible keys work`() { - assertThat(Named(Foo("bar"), "name").compatibilityKey) - .isEqualTo(Named(Foo("bar"), "name").compatibilityKey) - assertThat(Named(Foo("bar"), "name").compatibilityKey) - .isNotEqualTo(Named(Foo("baz"), "name").compatibilityKey) - } -} diff --git a/workflow-ui/internal-testing-android/api/internal-testing-android.api b/workflow-ui/internal-testing-android/api/internal-testing-android.api index 31b13f38e9..b7b5fa73de 100644 --- a/workflow-ui/internal-testing-android/api/internal-testing-android.api +++ b/workflow-ui/internal-testing-android/api/internal-testing-android.api @@ -72,21 +72,6 @@ public final class com/squareup/workflow1/ui/internal/test/IdlingDispatcherRule public static final field INSTANCE Lcom/squareup/workflow1/ui/internal/test/IdlingDispatcherRule; } -public class com/squareup/workflow1/ui/internal/test/LegacyWorkflowUiTestActivity : androidx/appcompat/app/AppCompatActivity { - public field viewEnvironment Lcom/squareup/workflow1/ui/ViewEnvironment; - public fun ()V - public final fun getCustomNonConfigurationData ()Ljava/util/Map; - public final fun getRestoreRenderingAfterConfigChange ()Z - public final fun getRootRenderedView ()Landroid/view/View; - public final fun getViewEnvironment ()Lcom/squareup/workflow1/ui/ViewEnvironment; - protected fun onCreate (Landroid/os/Bundle;)V - public final fun onRetainCustomNonConfigurationInstance ()Ljava/lang/Object; - public final fun recreateViewsOnNextRendering ()V - public final fun setRendering (Ljava/lang/Object;)Landroid/view/View; - public final fun setRestoreRenderingAfterConfigChange (Z)V - public final fun setViewEnvironment (Lcom/squareup/workflow1/ui/ViewEnvironment;)V -} - public class com/squareup/workflow1/ui/internal/test/WorkflowUiTestActivity : androidx/appcompat/app/AppCompatActivity { public field viewEnvironment Lcom/squareup/workflow1/ui/ViewEnvironment; public fun ()V diff --git a/workflow-ui/internal-testing-android/src/main/AndroidManifest.xml b/workflow-ui/internal-testing-android/src/main/AndroidManifest.xml index 09bce65b3b..007fcc0523 100644 --- a/workflow-ui/internal-testing-android/src/main/AndroidManifest.xml +++ b/workflow-ui/internal-testing-android/src/main/AndroidManifest.xml @@ -5,9 +5,5 @@ android:name=".WorkflowUiTestActivity" android:theme="@style/Theme.AppCompat.NoActionBar" /> - diff --git a/workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/LegacyWorkflowUiTestActivity.kt b/workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/LegacyWorkflowUiTestActivity.kt deleted file mode 100644 index f94a17e5da..0000000000 --- a/workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/LegacyWorkflowUiTestActivity.kt +++ /dev/null @@ -1,114 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.squareup.workflow1.ui.internal.test - -import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import com.squareup.workflow1.ui.Named -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.WorkflowViewStub - -/** - * Helper for testing workflow-ui code in UI tests. - * - * The content view of the activity is a [WorkflowViewStub], which you can control by calling - * [setRendering]. - * - * Typical usage: - * 1. Create an `ActivityScenarioRule` or `AndroidComposeRule` and pass this activity type. - * 2. In your `@Before` method, set the [viewEnvironment]. - * 3. In your tests, call [setRendering] to update the stub. - * - * You can also test configuration changes by calling `ActivityScenarioRule.recreate()`. By default, - * the [viewEnvironment] and last rendering will be restored when the view is re-created. You can - * also retain your own data by mutating [customNonConfigurationData]. If you don't want the - * rendering to be automatically restored, set [restoreRenderingAfterConfigChange] to false before - * calling `recreate()`. - */ -@WorkflowUiExperimentalApi -public open class LegacyWorkflowUiTestActivity : AppCompatActivity() { - - private val rootStub by lazy { WorkflowViewStub(this) } - private var renderingCounter = 0 - private lateinit var lastRendering: Any - - /** - * The [ViewEnvironment] used to create views for renderings passed to [setRendering]. - * This *must* be set before the first call to [setRendering]. - * Once set, the value is retained across configuration changes. - */ - public lateinit var viewEnvironment: ViewEnvironment - - /** - * The [View] that was created to display the last rendering passed to [setRendering]. - */ - public val rootRenderedView: View get() = rootStub.actual - - /** - * Key-value store for custom values that should be retained across configuration changes. - * Use this instead of using [getLastNonConfigurationInstance] or - * [getLastCustomNonConfigurationInstance] directly. - */ - public val customNonConfigurationData: MutableMap = mutableMapOf() - - /** - * Simulates the effect of having the activity backed by a real workflow runtime – remembers the - * actual render instance across recreation and will immediately set it on the new container in - * [onCreate]. - * - * True by default. If you need to change, do so before calling `recreate()`. - */ - public var restoreRenderingAfterConfigChange: Boolean = true - - /** - * Causes the next [setRendering] call to force a new view to be created, even if it otherwise wouldn't - * be (i.e. because the rendering is compatible with the previous one). - */ - public fun recreateViewsOnNextRendering() { - renderingCounter++ - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(rootStub) - - (lastCustomNonConfigurationInstance as NonConfigurationData?)?.let { data -> - viewEnvironment = data.viewEnvironment - customNonConfigurationData.apply { - clear() - putAll(data.customData) - } - // setRendering must be called last since it may consume the other values. - data.lastRendering?.let(::setRendering) - } - } - - final override fun onRetainCustomNonConfigurationInstance(): Any = NonConfigurationData( - viewEnvironment = viewEnvironment, - lastRendering = lastRendering.takeIf { restoreRenderingAfterConfigChange }, - customData = customNonConfigurationData, - ) - - /** - * Updates the [WorkflowViewStub] to a new rendering value. - * - * If [recreateViewsOnNextRendering] was previously called, the old view tree will be torn down - * and re-created from scratch. - */ - public fun setRendering(rendering: Any): View { - lastRendering = rendering - val named = Named( - wrapped = rendering, - name = renderingCounter.toString() - ) - return rootStub.update(named, viewEnvironment) - } - - private class NonConfigurationData( - val viewEnvironment: ViewEnvironment, - val lastRendering: Any?, - val customData: MutableMap, - ) -} diff --git a/workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/WorkflowUiTestActivity.kt b/workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/WorkflowUiTestActivity.kt index aacf46fa79..1038b6ad93 100644 --- a/workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/WorkflowUiTestActivity.kt +++ b/workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/WorkflowUiTestActivity.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") - package com.squareup.workflow1.ui.internal.test import android.os.Bundle @@ -75,6 +73,7 @@ public open class WorkflowUiTestActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(rootStub) + @Suppress("DEPRECATION") (lastCustomNonConfigurationInstance as NonConfigurationData?)?.let { data -> viewEnvironment = data.viewEnvironment customNonConfigurationData.apply { @@ -86,6 +85,7 @@ public open class WorkflowUiTestActivity : AppCompatActivity() { } } + @Deprecated("Deprecated in Java") final override fun onRetainCustomNonConfigurationInstance(): Any = NonConfigurationData( viewEnvironment = viewEnvironment, lastRendering = lastRendering.takeIf { restoreRenderingAfterConfigChange }, diff --git a/workflow-ui/radiography/api/radiography.api b/workflow-ui/radiography/api/radiography.api index 9a5eec7760..c746e1ea7d 100644 --- a/workflow-ui/radiography/api/radiography.api +++ b/workflow-ui/radiography/api/radiography.api @@ -1,5 +1,4 @@ public final class com/squareup/workflow1/ui/radiography/WorkflowViewRendererKt { - public static final fun getWorkflowViewRenderer (Lradiography/ViewStateRenderers;)Lradiography/ViewStateRenderer; public static final fun getWorkflowViewStateRenderer ()Lradiography/ViewStateRenderer; } diff --git a/workflow-ui/radiography/build.gradle.kts b/workflow-ui/radiography/build.gradle.kts index e39ea48d8a..57ae06267a 100644 --- a/workflow-ui/radiography/build.gradle.kts +++ b/workflow-ui/radiography/build.gradle.kts @@ -14,8 +14,6 @@ dependencies { androidTestImplementation(libs.androidx.test.core) androidTestImplementation(libs.androidx.test.truth) - androidTestImplementation(project(":workflow-ui:container-android")) - implementation(libs.squareup.radiography) implementation(project(":workflow-ui:core-android")) diff --git a/workflow-ui/radiography/src/main/java/com/squareup/workflow1/ui/radiography/WorkflowViewRenderer.kt b/workflow-ui/radiography/src/main/java/com/squareup/workflow1/ui/radiography/WorkflowViewRenderer.kt index 43c0e4987b..c13c103c90 100644 --- a/workflow-ui/radiography/src/main/java/com/squareup/workflow1/ui/radiography/WorkflowViewRenderer.kt +++ b/workflow-ui/radiography/src/main/java/com/squareup/workflow1/ui/radiography/WorkflowViewRenderer.kt @@ -1,27 +1,13 @@ -@file:Suppress("DEPRECATION") - package com.squareup.workflow1.ui.radiography import com.squareup.workflow1.ui.Compatible -import com.squareup.workflow1.ui.Named import com.squareup.workflow1.ui.NamedScreen import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.getRendering import com.squareup.workflow1.ui.screenOrNull import radiography.AttributeAppendable import radiography.ScannableView import radiography.ScannableView.AndroidView import radiography.ViewStateRenderer -import radiography.ViewStateRenderers - -/** - * Renders information about views that were created by workflow-ui, i.e. views - * that return non-null values from [getRendering] or [screenOrNull]. - */ -@Suppress("unused") -@Deprecated("Use WorkflowViewStateRenderer", ReplaceWith("WorkflowViewStateRenderer")) -public val ViewStateRenderers.WorkflowViewRenderer: ViewStateRenderer - get() = WorkflowViewStateRenderer public val WorkflowViewStateRenderer: ViewStateRenderer get() = WorkflowViewRendererImpl @@ -31,14 +17,12 @@ private object WorkflowViewRendererImpl : ViewStateRenderer { override fun AttributeAppendable.render(view: ScannableView) { val androidView = (view as? AndroidView)?.view ?: return - val rendering = androidView.getRendering() ?: androidView.screenOrNull ?: return + val rendering = androidView.screenOrNull ?: return renderRendering(rendering) } private fun AttributeAppendable.renderRendering(rendering: Any) { - val actualRendering = (rendering as? Named<*>)?.wrapped - ?: (rendering as? NamedScreen<*>)?.content - ?: rendering + val actualRendering = (rendering as? NamedScreen<*>)?.content ?: rendering append("workflow-rendering-type:${actualRendering::class.java.name}") if (rendering is Compatible) {