Skip to content

Commit 4e63f72

Browse files
committed
Introduces AndroidViewRendering, de-emphasizes ViewRegistry
`AndroidViewRendering` is implemented by renderings that can provide their own `ViewFactory`. `ViewRegistry.buildView` looks to see if a given rendering implements that method if it fails to find an entry for it -- note that we look for a registration first, so `ViewRegistry` is able override the default. Given this, it's no longer crucial for developers to define and provide a `ViewRegistry`, so we get rid of all the nannying that forced them to provide one.
1 parent 679256c commit 4e63f72

File tree

12 files changed

+66
-186
lines changed

12 files changed

+66
-186
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.squareup.sample.helloworkflow
2+
3+
import com.squareup.sample.helloworkflow.databinding.HelloGoodbyeLayoutBinding
4+
import com.squareup.workflow1.ui.AndroidViewRendering
5+
import com.squareup.workflow1.ui.LayoutRunner
6+
import com.squareup.workflow1.ui.ViewFactory
7+
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
8+
9+
@OptIn(WorkflowUiExperimentalApi::class)
10+
data class HelloView(
11+
val message: String,
12+
val onClick: () -> Unit
13+
) : AndroidViewRendering<HelloView> {
14+
override val viewFactory: ViewFactory<HelloView> =
15+
LayoutRunner.bind(HelloGoodbyeLayoutBinding::inflate) { r, _ ->
16+
helloMessage.text = r.message
17+
helloMessage.setOnClickListener { r.onClick() }
18+
}
19+
}

samples/hello-workflow/src/main/java/com/squareup/sample/helloworkflow/HelloViewFactory.kt

Lines changed: 0 additions & 29 deletions
This file was deleted.

samples/hello-workflow/src/main/java/com/squareup/sample/helloworkflow/HelloWorkflow.kt

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,5 @@
1-
/*
2-
* Copyright 2019 Square Inc.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
161
package com.squareup.sample.helloworkflow
172

18-
import com.squareup.sample.helloworkflow.HelloWorkflow.Rendering
193
import com.squareup.sample.helloworkflow.HelloWorkflow.State
204
import com.squareup.sample.helloworkflow.HelloWorkflow.State.Goodbye
215
import com.squareup.sample.helloworkflow.HelloWorkflow.State.Hello
@@ -24,31 +8,26 @@ import com.squareup.workflow1.StatefulWorkflow
248
import com.squareup.workflow1.action
259
import com.squareup.workflow1.parse
2610

27-
object HelloWorkflow : StatefulWorkflow<Unit, State, Nothing, Rendering>() {
11+
object HelloWorkflow : StatefulWorkflow<Unit, State, Nothing, HelloView>() {
2812
enum class State {
2913
Hello,
3014
Goodbye
3115
}
3216

33-
data class Rendering(
34-
val message: String,
35-
val onClick: () -> Unit
36-
)
37-
3817
override fun initialState(
3918
props: Unit,
4019
snapshot: Snapshot?
4120
): State = snapshot?.bytes?.parse { source -> if (source.readInt() == 1) Hello else Goodbye }
42-
?: Hello
21+
?: Hello
4322

4423
override fun render(
4524
props: Unit,
4625
state: State,
4726
context: RenderContext
48-
): Rendering {
49-
return Rendering(
50-
message = state.name,
51-
onClick = { context.actionSink.send(helloAction) }
27+
): HelloView {
28+
return HelloView(
29+
message = state.name,
30+
onClick = { context.actionSink.send(helloAction) }
5231
)
5332
}
5433

samples/hello-workflow/src/main/java/com/squareup/sample/helloworkflow/HelloWorkflowActivity.kt

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,17 @@
1-
/*
2-
* Copyright 2019 Square Inc.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
161
package com.squareup.sample.helloworkflow
172

183
import android.os.Bundle
194
import androidx.appcompat.app.AppCompatActivity
205
import com.squareup.workflow1.SimpleLoggingWorkflowInterceptor
216
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
22-
import com.squareup.workflow1.ui.ViewRegistry
237
import com.squareup.workflow1.ui.WorkflowRunner
248
import com.squareup.workflow1.ui.setContentWorkflow
259

26-
@OptIn(WorkflowUiExperimentalApi::class)
27-
private val viewRegistry = ViewRegistry(HelloViewFactory)
28-
2910
@OptIn(WorkflowUiExperimentalApi::class)
3011
class HelloWorkflowActivity : AppCompatActivity() {
3112
override fun onCreate(savedInstanceState: Bundle?) {
3213
super.onCreate(savedInstanceState)
33-
setContentWorkflow(viewRegistry) {
14+
setContentWorkflow {
3415
WorkflowRunner.Config(
3516
HelloWorkflow,
3617
interceptors = listOf(SimpleLoggingWorkflowInterceptor())
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.squareup.workflow1.ui
2+
3+
@WorkflowUiExperimentalApi
4+
interface AndroidViewRendering<V : AndroidViewRendering<V>> {
5+
val viewFactory: ViewFactory<V>
6+
}

workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/CompositeViewRegistry.kt

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,7 @@ internal class CompositeViewRegistry private constructor(
4242

4343
override fun <RenderingT : Any> getFactoryFor(
4444
renderingType: KClass<out RenderingT>
45-
): ViewFactory<RenderingT> = getRegistryFor(renderingType).getFactoryFor(renderingType)
46-
47-
private fun getRegistryFor(renderingType: KClass<out Any>): ViewRegistry {
48-
return requireNotNull(registriesByKey[renderingType]) {
49-
"A ${ViewFactory::class.java.name} should have been registered " +
50-
"to display a $renderingType."
51-
}
52-
}
45+
): ViewFactory<RenderingT>? = registriesByKey[renderingType]?.getFactoryFor(renderingType)
5346

5447
companion object {
5548
private fun mergeRegistries(vararg registries: ViewRegistry): Map<KClass<*>, ViewRegistry> {

workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/NamedViewFactory.kt

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,3 @@
1-
/*
2-
* Copyright 2019 Square Inc.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
161
package com.squareup.workflow1.ui
172

183
/**

workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/TypedViewRegistry.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,8 @@ internal class TypedViewRegistry private constructor(
4040

4141
override fun <RenderingT : Any> getFactoryFor(
4242
renderingType: KClass<out RenderingT>
43-
): ViewFactory<RenderingT> {
43+
): ViewFactory<RenderingT>? {
4444
@Suppress("UNCHECKED_CAST")
45-
return requireNotNull(bindings[renderingType] as? ViewFactory<RenderingT>) {
46-
"A ${ViewFactory::class.java.name} should have been registered " +
47-
"to display a $renderingType."
48-
}
45+
return bindings[renderingType] as? ViewFactory<RenderingT>
4946
}
5047
}

workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewEnvironment.kt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,11 @@ typealias ContainerHints = ViewEnvironment
3030
* its children via [View.showRendering][android.view.View.showRendering] et al.
3131
* Allows container views to give descendants information about the context in which
3232
* they're drawing.
33-
*
34-
* Every [ViewEnvironment] includes a [ViewRegistry]. This allows container views to
35-
* make recursive [ViewRegistry.buildView] calls to build child views to show nested renderings.
3633
*/
3734
@WorkflowUiExperimentalApi
38-
class ViewEnvironment private constructor(
39-
private val map: Map<ViewEnvironmentKey<*>, Any>
35+
class ViewEnvironment(
36+
val map: Map<ViewEnvironmentKey<*>, Any> = emptyMap()
4037
) {
41-
constructor(registry: ViewRegistry) :
42-
this(mapOf<ViewEnvironmentKey<*>, Any>(ViewRegistry to registry))
43-
4438
@Suppress("UNCHECKED_CAST")
4539
operator fun <T : Any> get(key: ViewEnvironmentKey<T>): T = map[key] as? T ?: key.default
4640

workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/ViewRegistry.kt

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,15 @@ interface ViewRegistry {
7070
/**
7171
* This method is not for general use, use [WorkflowViewStub] instead.
7272
*
73-
* Returns the [ViewFactory] that was registered for the given [renderingType].
74-
*
75-
* @throws IllegalArgumentException if no factory can be found for type [RenderingT]
73+
* Returns the [ViewFactory] that was registered for the given [renderingType], or null
74+
* if none was found.
7675
*/
7776
fun <RenderingT : Any> getFactoryFor(
7877
renderingType: KClass<out RenderingT>
79-
): ViewFactory<RenderingT>
78+
): ViewFactory<RenderingT>?
8079

8180
companion object : ViewEnvironmentKey<ViewRegistry>(ViewRegistry::class) {
82-
override val default: ViewRegistry
83-
get() = error("There should always be a ViewRegistry hint, this is bug in Workflow.")
81+
override val default: ViewRegistry get() = ViewRegistry()
8482
}
8583
}
8684

@@ -105,7 +103,7 @@ fun ViewRegistry(): ViewRegistry = TypedViewRegistry()
105103
* It is usually more convenient to use [WorkflowViewStub] than to call this method directly.
106104
*
107105
* Creates a [View] to display [initialRendering], which can be updated via calls
108-
* to [View.showRendering].
106+
* to [View.showRendering]. Uses
109107
*
110108
* @throws IllegalArgumentException if no factory can be find for type [RenderingT]
111109
*
@@ -119,19 +117,27 @@ fun <RenderingT : Any> ViewRegistry.buildView(
119117
contextForNewView: Context,
120118
container: ViewGroup? = null
121119
): View {
122-
return getFactoryFor(initialRendering::class)
123-
.buildView(
124-
initialRendering,
125-
initialViewEnvironment,
126-
contextForNewView,
127-
container
128-
)
129-
.apply {
130-
check(this.getRendering<Any>() != null) {
131-
"View.bindShowRendering should have been called for $this, typically by the " +
132-
"${ViewFactory::class.java.name} that created it."
133-
}
120+
@Suppress("UNCHECKED_CAST")
121+
val factory: ViewFactory<RenderingT> = getFactoryFor(initialRendering::class)
122+
?: (initialRendering as? AndroidViewRendering<*>)?.viewFactory as ViewFactory<RenderingT>
123+
?: throw IllegalArgumentException(
124+
"A ${ViewFactory::class.qualifiedName} should have been registered " +
125+
"to display ${initialRendering::class.qualifiedName} instances, or that class " +
126+
"should implement ${ViewFactory::class.simpleName}<${initialRendering::class.simpleName}>."
127+
)
128+
129+
return factory.buildView(
130+
initialRendering,
131+
initialViewEnvironment,
132+
contextForNewView,
133+
container
134+
)
135+
.apply {
136+
check(this.getRendering<Any>() != null) {
137+
"View.bindShowRendering should have been called for $this, typically by the " +
138+
"${ViewFactory::class.java.name} that created it."
134139
}
140+
}
135141
}
136142

137143
/**

0 commit comments

Comments
 (0)