Skip to content

Commit b3b6bf1

Browse files
committed
BREAKING: Replaces ComposeScreenViewFactory with ScreenComposableFactory
This commit takes advantage of the new `ViewRegistry.Key`, which allows renderings to be bound to multiple UI factories, to fix a long standing problem where wrapper screens -- things like `NamedScreen` and `EnvironmentScreen` -- used in a Compose context would cause needless calls to `@Composeable fun AndroidView()` and `ComposeView()`. For example, consider this rendering: ``` BodyAndOverlaysScreen( body = SomeComposeScreen( EnvironmentScreen( SomeOtherComposeScreen ) ) ) ``` Before this change, that would create a View hierarchy something like this: ``` BodyAndOverlaysContainer : FrameLayout { mChildren[0] = ComposeView { // compose land SomeComposeScreen.Content { AndroidView { ComposeView { // nested compose land SomeOtherComposeScreen.Content() ``` Now it will look this way: ``` BodyAndOverlaysContainer : FrameLayout { mChildren[0] = ComposeView { // compose land SomeComposeScreen.Content { SomeOtherComposeScreen.Content() ``` `ScreenComposableFactory` replaces `ComposeScreenViewFactory`, and `ComposeScreen` no longer extends `AndroidScreen`. Compose support is now a first class citizen, instead of a hack bolted on to View support. Unfortunately, `ViewEnvironment.withComposeInteropSupport()` (see below) now must be called near the root of a Workflow app to enable the seamless Compose > Classic > Compose handling that used to be built in to `WorkflowRendering` and `ComposeScreenViewFactory`. This means that call is required for Compose support for built in rendering types like `BodyAndOverlaysScreen` and `BackStackScreen`, which so far are backed only by classic View implementations. Other introductions, changes: - `Screen.toComposableFactory()`, used by `WorkflowRendering()` in the same way that `WorkflowViewStub` uses `Screen.toViewFactory()` - `ScreenComposableFactoryFinder`, a `ViewEnvironment`-based strategy object used by `Screen.toComposableFactory()` the same way that `Screen.toViewFactory()` uses `ScreenViewFactoryFinder`. The default implementation provides Compose bindings for `NamedScreen` and `EnvironmentScreen`, fixing #546. - `ScreenViewFactoryFinder.getViewFactoryForRendering()` can now return `null`. A `requireViewFactoryForRendering()` extension is introduced for use when `null` is not acceptable. - `ViewEnvironment.withComposeInteropSupport()`, which wraps the found `ScreenComposableFactoryFinder` and `ScreenViewFactoryFinder` with implementations that allow Compose contexts to handle renderings bound only to `ScreenViewFactory`, and classic contexts to handle renderings bound only to `ScreenComposableFactory`. Replaces the logic that used to be in the private `ScreenViewFactory.asComposeViewFactory()` extension in `WorkflowRendering()`. Fixes #546
1 parent 6bb3516 commit b3b6bf1

File tree

35 files changed

+825
-672
lines changed

35 files changed

+825
-672
lines changed

samples/compose-samples/src/main/java/com/squareup/sample/compose/hellocompose/HelloBinding.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import androidx.compose.ui.Alignment
88
import androidx.compose.ui.Modifier
99
import com.squareup.sample.compose.hellocompose.HelloWorkflow.Rendering
1010
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
11-
import com.squareup.workflow1.ui.compose.composeScreenViewFactory
11+
import com.squareup.workflow1.ui.compose.screenComposableFactory
1212

1313
@OptIn(WorkflowUiExperimentalApi::class)
14-
val HelloBinding = composeScreenViewFactory<Rendering> { rendering, _ ->
14+
val HelloBinding = screenComposableFactory<Rendering> { rendering, _ ->
1515
Text(
1616
rendering.message,
1717
modifier = Modifier

samples/compose-samples/src/main/java/com/squareup/sample/compose/hellocomposebinding/HelloBinding.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import androidx.compose.ui.Modifier
99
import androidx.compose.ui.tooling.preview.Preview
1010
import com.squareup.sample.compose.hellocomposebinding.HelloWorkflow.Rendering
1111
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
12-
import com.squareup.workflow1.ui.compose.composeScreenViewFactory
12+
import com.squareup.workflow1.ui.compose.screenComposableFactory
1313
import com.squareup.workflow1.ui.compose.tooling.Preview
1414

1515
@OptIn(WorkflowUiExperimentalApi::class)
16-
val HelloBinding = composeScreenViewFactory<Rendering> { rendering, _ ->
16+
val HelloBinding = screenComposableFactory<Rendering> { rendering, _ ->
1717
Text(
1818
rendering.message,
1919
modifier = Modifier

samples/compose-samples/src/main/java/com/squareup/sample/compose/hellocomposebinding/HelloBindingActivity.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import com.squareup.workflow1.ui.ViewEnvironment
1717
import com.squareup.workflow1.ui.ViewRegistry
1818
import com.squareup.workflow1.ui.WorkflowLayout
1919
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
20+
import com.squareup.workflow1.ui.compose.withComposeInteropSupport
2021
import com.squareup.workflow1.ui.compose.withCompositionRoot
2122
import com.squareup.workflow1.ui.plus
2223
import com.squareup.workflow1.ui.renderWorkflowIn
@@ -25,13 +26,15 @@ import kotlinx.coroutines.flow.StateFlow
2526

2627
@OptIn(WorkflowUiExperimentalApi::class)
2728
private val viewEnvironment =
28-
(ViewEnvironment.EMPTY + ViewRegistry(HelloBinding)).withCompositionRoot { content ->
29-
MaterialTheme(content = content)
30-
}
29+
(ViewEnvironment.EMPTY + ViewRegistry(HelloBinding))
30+
.withCompositionRoot { content ->
31+
MaterialTheme(content = content)
32+
}
33+
.withComposeInteropSupport()
3134

3235
/**
3336
* Demonstrates how to create and display a view factory with
34-
* [composeScreenViewFactory][com.squareup.workflow1.ui.compose.composeScreenViewFactory].
37+
* [screenComposableFactory][com.squareup.workflow1.ui.compose.screenComposableFactory].
3538
*/
3639
class HelloBindingActivity : AppCompatActivity() {
3740
@OptIn(WorkflowUiExperimentalApi::class)

samples/compose-samples/src/main/java/com/squareup/sample/compose/hellocomposeworkflow/HelloComposeWorkflowActivity.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ import androidx.lifecycle.ViewModel
1010
import androidx.lifecycle.viewModelScope
1111
import com.squareup.workflow1.WorkflowExperimentalRuntime
1212
import com.squareup.workflow1.config.AndroidRuntimeConfigTools
13+
import com.squareup.workflow1.mapRendering
1314
import com.squareup.workflow1.ui.Screen
15+
import com.squareup.workflow1.ui.ViewEnvironment
1416
import com.squareup.workflow1.ui.WorkflowLayout
1517
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
18+
import com.squareup.workflow1.ui.compose.withComposeInteropSupport
19+
import com.squareup.workflow1.ui.container.withEnvironment
1620
import com.squareup.workflow1.ui.renderWorkflowIn
1721
import kotlinx.coroutines.flow.StateFlow
1822

@@ -30,7 +34,9 @@ class HelloComposeWorkflowActivity : AppCompatActivity() {
3034
@OptIn(WorkflowUiExperimentalApi::class)
3135
val renderings: StateFlow<Screen> by lazy {
3236
renderWorkflowIn(
33-
workflow = HelloWorkflow,
37+
workflow = HelloWorkflow.mapRendering {
38+
it.withEnvironment(ViewEnvironment.EMPTY.withComposeInteropSupport())
39+
},
3440
scope = viewModelScope,
3541
savedStateHandle = savedState,
3642
runtimeConfig = AndroidRuntimeConfigTools.getAppWorkflowRuntimeConfig()

samples/compose-samples/src/main/java/com/squareup/sample/compose/inlinerendering/InlineRenderingActivity.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ import androidx.lifecycle.ViewModel
1010
import androidx.lifecycle.viewModelScope
1111
import com.squareup.workflow1.WorkflowExperimentalRuntime
1212
import com.squareup.workflow1.config.AndroidRuntimeConfigTools
13+
import com.squareup.workflow1.mapRendering
1314
import com.squareup.workflow1.ui.Screen
15+
import com.squareup.workflow1.ui.ViewEnvironment
1416
import com.squareup.workflow1.ui.WorkflowLayout
1517
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
18+
import com.squareup.workflow1.ui.compose.withComposeInteropSupport
19+
import com.squareup.workflow1.ui.container.withEnvironment
1620
import com.squareup.workflow1.ui.renderWorkflowIn
1721
import kotlinx.coroutines.flow.StateFlow
1822

@@ -34,7 +38,9 @@ class InlineRenderingActivity : AppCompatActivity() {
3438
@OptIn(WorkflowUiExperimentalApi::class)
3539
val renderings: StateFlow<Screen> by lazy {
3640
renderWorkflowIn(
37-
workflow = InlineRenderingWorkflow,
41+
workflow = InlineRenderingWorkflow.mapRendering {
42+
it.withEnvironment(ViewEnvironment.EMPTY.withComposeInteropSupport())
43+
},
3844
scope = viewModelScope,
3945
savedStateHandle = savedState,
4046
runtimeConfig = AndroidRuntimeConfigTools.getAppWorkflowRuntimeConfig()

samples/compose-samples/src/main/java/com/squareup/sample/compose/inlinerendering/InlineRenderingWorkflow.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ import com.squareup.workflow1.StatefulWorkflow
2121
import com.squareup.workflow1.WorkflowExperimentalRuntime
2222
import com.squareup.workflow1.config.AndroidRuntimeConfigTools
2323
import com.squareup.workflow1.parse
24-
import com.squareup.workflow1.ui.AndroidScreen
24+
import com.squareup.workflow1.ui.Screen
2525
import com.squareup.workflow1.ui.ViewEnvironment
2626
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
2727
import com.squareup.workflow1.ui.compose.ComposeScreen
2828
import com.squareup.workflow1.ui.compose.WorkflowRendering
2929
import com.squareup.workflow1.ui.compose.renderAsState
3030

31-
object InlineRenderingWorkflow : StatefulWorkflow<Unit, Int, Nothing, AndroidScreen<*>>() {
31+
object InlineRenderingWorkflow : StatefulWorkflow<Unit, Int, Nothing, Screen>() {
3232

3333
override fun initialState(
3434
props: Unit,
@@ -39,7 +39,7 @@ object InlineRenderingWorkflow : StatefulWorkflow<Unit, Int, Nothing, AndroidScr
3939
renderProps: Unit,
4040
renderState: Int,
4141
context: RenderContext
42-
): AndroidScreen<*> = ComposeScreen {
42+
) = ComposeScreen {
4343
Box {
4444
Button(onClick = context.eventHandler { state += 1 }) {
4545
Text("Counter: ")

samples/compose-samples/src/main/java/com/squareup/sample/compose/launcher/Samples.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ val samples = listOf(
3737
Sample(
3838
"Hello Compose Binding",
3939
HelloBindingActivity::class,
40-
"Creates a ViewFactory using composeViewFactory."
40+
"Creates a ScreenComposableFactory using screenComposableFactory()."
4141
) { DrawHelloRenderingPreview() },
4242
Sample(
4343
"Nested Renderings",

samples/compose-samples/src/main/java/com/squareup/sample/compose/nestedrenderings/LegacyRunner.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.squareup.workflow1.ui.ScreenViewFactory.Companion.fromViewBinding
1111
import com.squareup.workflow1.ui.ScreenViewRunner
1212
import com.squareup.workflow1.ui.ViewEnvironment
1313
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
14+
import com.squareup.workflow1.ui.compose.asComposableFactory
1415
import com.squareup.workflow1.ui.compose.tooling.Preview
1516

1617
/**
@@ -36,7 +37,7 @@ class LegacyRunner(private val binding: LegacyViewBinding) : ScreenViewRunner<Le
3637
@Preview(widthDp = 200, heightDp = 150, showBackground = true)
3738
@Composable
3839
private fun LegacyRunnerPreview() {
39-
LegacyRunner.Preview(
40+
LegacyRunner.asComposableFactory().Preview(
4041
rendering = LegacyRendering(StringRendering("child")),
4142
placeholderModifier = Modifier.fillMaxSize()
4243
)

samples/compose-samples/src/main/java/com/squareup/sample/compose/nestedrenderings/NestedRenderingsActivity.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import com.squareup.workflow1.ui.ViewEnvironment
1818
import com.squareup.workflow1.ui.ViewRegistry
1919
import com.squareup.workflow1.ui.WorkflowLayout
2020
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
21+
import com.squareup.workflow1.ui.compose.withComposeInteropSupport
2122
import com.squareup.workflow1.ui.compose.withCompositionRoot
2223
import com.squareup.workflow1.ui.plus
2324
import com.squareup.workflow1.ui.renderWorkflowIn
@@ -32,11 +33,13 @@ private val viewRegistry = ViewRegistry(
3233

3334
@OptIn(WorkflowUiExperimentalApi::class)
3435
private val viewEnvironment =
35-
(ViewEnvironment.EMPTY + viewRegistry).withCompositionRoot { content ->
36-
CompositionLocalProvider(LocalBackgroundColor provides Color.Green) {
37-
content()
36+
(ViewEnvironment.EMPTY + viewRegistry)
37+
.withCompositionRoot { content ->
38+
CompositionLocalProvider(LocalBackgroundColor provides Color.Green) {
39+
content()
40+
}
3841
}
39-
}
42+
.withComposeInteropSupport()
4043

4144
@WorkflowUiExperimentalApi
4245
class NestedRenderingsActivity : AppCompatActivity() {

samples/compose-samples/src/main/java/com/squareup/sample/compose/nestedrenderings/RecursiveViewFactory.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import com.squareup.workflow1.ui.Screen
2727
import com.squareup.workflow1.ui.ViewEnvironment
2828
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
2929
import com.squareup.workflow1.ui.compose.WorkflowRendering
30-
import com.squareup.workflow1.ui.compose.composeScreenViewFactory
30+
import com.squareup.workflow1.ui.compose.screenComposableFactory
3131
import com.squareup.workflow1.ui.compose.tooling.Preview
3232

3333
/**
@@ -39,7 +39,7 @@ val LocalBackgroundColor = compositionLocalOf<Color> { error("No background colo
3939
* A `ViewFactory` that renders [RecursiveWorkflow.Rendering]s.
4040
*/
4141
@OptIn(WorkflowUiExperimentalApi::class)
42-
val RecursiveViewFactory = composeScreenViewFactory<Rendering> { rendering, viewEnvironment ->
42+
val RecursiveViewFactory = screenComposableFactory<Rendering> { rendering, viewEnvironment ->
4343
// Every child should be drawn with a slightly-darker background color.
4444
val color = LocalBackgroundColor.current
4545
val childColor = remember(color) {

0 commit comments

Comments
 (0)