diff --git a/README-templates.md b/README-templates.md new file mode 100644 index 0000000000..d9caf047c1 --- /dev/null +++ b/README-templates.md @@ -0,0 +1,8 @@ +# File Templates + +`workflow-file-templates.zip` can be imported into Android Studio / IntelliJ IDEA to add a few Workflow-specific file templates, via _File > Manage IDE Settings > Import Settingsā€¦_. + +To update the templates: + + * edit them in the IDE (_Settings > Editor > File and Code Templates_) + * export them (_File > Manage IDE Settings > Import Settingsā€¦_), taking care to clear every checkbox except that for File Templates. diff --git a/fileTemplates/Layout Runner (ViewBinding).kt b/fileTemplates/Layout Runner (ViewBinding).kt deleted file mode 100644 index 9b0f3af2ce..0000000000 --- a/fileTemplates/Layout Runner (ViewBinding).kt +++ /dev/null @@ -1,27 +0,0 @@ -## Unlike the Workflow Templates, we never generate inner classes for view bindings -## or rendering types. i.e. they are never "optional" -package ${PACKAGE_NAME} - -import com.squareup.workflow1.ui.LayoutRunner -import com.squareup.workflow1.ui.LayoutRunner.Companion.bind -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.ViewFactory -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi - -#parse("File Header.java") -@OptIn(WorkflowUiExperimentalApi::class) -class $Name( - private val binding: $VIEW_BINDING_TYPE -) : LayoutRunner<$RENDERING_TYPE> { - - override fun showRendering( - rendering: $RENDERING_TYPE, - viewEnvironment: ViewEnvironment - ) { - TODO("Update ViewBinding from rendering") - } - - companion object : ViewFactory<$RENDERING_TYPE> by bind( - $VIEW_BINDING_TYPE::inflate, ::$NAME - ) -} diff --git a/fileTemplates/Stateful Workflow.kt b/fileTemplates/Stateful Workflow.kt deleted file mode 100644 index 6f7d54c9f2..0000000000 --- a/fileTemplates/Stateful Workflow.kt +++ /dev/null @@ -1,87 +0,0 @@ -#set ($prefix = $NAME.replace('Workflow', '') ) -## build props string -#if( $PROPS_TYPE_OPTIONAL == '') - #set ($props_type = $prefix + "Props") -#else - #set ($props_type = $PROPS_TYPE_OPTIONAL) -#end -## build state string -#if( $STATE_TYPE_OPTIONAL == '') - #set ($state_type = $prefix + "State") -#else - #set ($state_type = $STATE_TYPE_OPTIONAL) -#end -## build output string -#if( $OUTPUT_TYPE_OPTIONAL == '') - #set ($output_type = $prefix + "Output") -#else - #set ($output_type = $OUTPUT_TYPE_OPTIONAL) -#end -## build rendering string -#if( $RENDERING_TYPE_OPTIONAL == '') - #set ($rendering_type = $prefix + "Rendering") -#else - #set ($rendering_type = $RENDERING_TYPE_OPTIONAL) -#end -package $PACKAGE_NAME - -import com.squareup.workflow1.Snapshot -import com.squareup.workflow1.StatefulWorkflow - -#if( $PROPS_TYPE_OPTIONAL == '') ## import if we create below -import $PACKAGE_NAME.$NAME.$props_type -#end -#if( $STATE_TYPE_OPTIONAL == '') ## import if we create below -import $PACKAGE_NAME.$NAME.$state_type -#end -#if( $OUTPUT_TYPE_OPTIONAL == '') ## import if we create below -import $PACKAGE_NAME.$NAME.$output_type -#end -#if( $RENDERING_TYPE_OPTIONAL == '') ## import if we create below -import $PACKAGE_NAME.$NAME.$rendering_type -#end - -#parse("File Header.java") -object $NAME : StatefulWorkflow<$props_type, $state_type, $output_type, $rendering_type>() { - - #if( $PROPS_TYPE_OPTIONAL == '') ## create if not supplied - data class $props_type( - // TODO add args - ) - #end - - #if( $STATE_TYPE_OPTIONAL == '') ## create if not supplied - data class $state_type( - // TODO add args - ) - #end - - #if( $OUTPUT_TYPE_OPTIONAL == '') ## create if not supplied - data class $output_type( - // TODO add args - ) - #end - - #if( $RENDERING_TYPE_OPTIONAL == '') ## create if not supplied - data class $rendering_type( - // TODO add args - ) - #end - - override fun initialState( - props: $props_type, - snapshot: Snapshot? - ): $state_type = TODO("Initialize state") - - override fun render( - renderProps: $props_type, - renderState: $state_type, - context: RenderContext - ): $rendering_type { - TODO("Render") - } - - override fun snapshotState(state: $state_type): Snapshot? = Snapshot.write { - TODO("Save state") - } -} diff --git a/fileTemplates/Stateless Workflow.kt b/fileTemplates/Stateless Workflow.kt deleted file mode 100644 index e103179e0e..0000000000 --- a/fileTemplates/Stateless Workflow.kt +++ /dev/null @@ -1,61 +0,0 @@ -#set ($prefix = $NAME.replace('Workflow', '') ) -## build props string -#if( $PROPS_TYPE_OPTIONAL == '') - #set ($props_type = $prefix + "Props") -#else - #set ($props_type = $PROPS_TYPE_OPTIONAL) -#end -## build output string -#if( $OUTPUT_TYPE_OPTIONAL == '') - #set ($output_type = $prefix + "Output") -#else - #set ($output_type = $OUTPUT_TYPE_OPTIONAL) -#end -## build rendering string -#if( $RENDERING_TYPE_OPTIONAL == '') - #set ($rendering_type = $prefix + "Rendering") -#else - #set ($rendering_type = $RENDERING_TYPE_OPTIONAL) -#end -package $PACKAGE_NAME - -import com.squareup.workflow1.StatelessWorkflow - -#if( $PROPS_TYPE_OPTIONAL == '') ## import if we create below -import $PACKAGE_NAME.$NAME.$props_type -#end -#if( $OUTPUT_TYPE_OPTIONAL == '') ## import if we create below -import $PACKAGE_NAME.$NAME.$output_type -#end -#if( $RENDERING_TYPE_OPTIONAL == '') ## import if we create below -import $PACKAGE_NAME.$NAME.$rendering_type -#end - -#parse("File Header.java") -object $NAME : StatelessWorkflow<$props_type, $output_type, $rendering_type>() { - - #if( $PROPS_TYPE_OPTIONAL == '') ## create if not supplied - data class $props_type( - // TODO add args - ) - #end - - #if( $OUTPUT_TYPE_OPTIONAL == '') ## create if not supplied - data class $output_type( - // TODO add args - ) - #end - - #if( $RENDERING_TYPE_OPTIONAL == '') ## create if not supplied - data class $rendering_type( - // TODO add args - ) - #end - - override fun render( - renderProps: $props_type, - context: RenderContext - ): $rendering_type { - TODO("Render") - } -} diff --git a/install-templates.sh b/install-templates.sh deleted file mode 100755 index 3c012c9aa6..0000000000 --- a/install-templates.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Installs Workflow file templates for IntelliJ and Android Studio. - - -OS="$(uname -s)" -echo "Installing Workflow file templates on $OS system..." -ideaConfigPath="" -if [[ "$OS" == Linux ]]; then - ideaConfigPath="$HOME/.config" -elif [[ "$OS" == Darwin ]]; then - ideaConfigPath="$HOME/Library/Application Support" -fi -TEMPLATES="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/fileTemplates" -for i in "$ideaConfigPath"/Google/AndroidStudio* \ - "$ideaConfigPath"/JetBrains/IdeaIC* \ - "$ideaConfigPath"/JetBrains/IntelliJIdea* -do - echo $i - if [[ -d "$i" ]]; then - mkdir -p "$i/fileTemplates" - cp -frv "$TEMPLATES"/* "$i/fileTemplates" - fi -done - -echo "Done." -echo "" -echo "Restart IntelliJ and/or AndroidStudio." diff --git a/samples/tutorial/tutorial-1-complete/src/main/java/workflow/tutorial/WelcomeScreenRunner.kt b/samples/tutorial/tutorial-1-complete/src/main/java/workflow/tutorial/WelcomeScreenRunner.kt new file mode 100644 index 0000000000..ee016ed034 --- /dev/null +++ b/samples/tutorial/tutorial-1-complete/src/main/java/workflow/tutorial/WelcomeScreenRunner.kt @@ -0,0 +1,38 @@ +package workflow.tutorial + +import com.squareup.workflow1.ui.LayoutRunner +import com.squareup.workflow1.ui.LayoutRunner.Companion.bind +import com.squareup.workflow1.ui.ViewEnvironment +import com.squareup.workflow1.ui.ViewFactory +import com.squareup.workflow1.ui.WorkflowUiExperimentalApi +import com.squareup.workflow1.ui.setTextChangedListener +import com.squareup.workflow1.ui.updateText +import workflow.tutorial.views.databinding.WelcomeViewBinding + +@OptIn(WorkflowUiExperimentalApi::class) +class WelcomeScreenRunner( + private val welcomeBinding: WelcomeViewBinding +) : LayoutRunner { + + override fun showRendering( + rendering: WelcomeScreen, + viewEnvironment: ViewEnvironment + ) { + // updateText and setTextChangedListener are helpers provided by the workflow library that take + // care of the complexity of correctly interacting with EditTexts in a declarative manner. + welcomeBinding.username.updateText(rendering.username) + welcomeBinding.username.setTextChangedListener { + rendering.onUsernameChanged(it.toString()) + } + welcomeBinding.login.setOnClickListener { rendering.onLoginTapped() } + } + + /** + * Define a [ViewFactory] that will inflate an instance of [WelcomeViewBinding] and an instance + * of [WelcomeScreenRunner] when asked, then wire them up so that [showRendering] will be called + * whenever the workflow emits a new [WelcomeScreen]. + */ + companion object : ViewFactory by bind( + WelcomeViewBinding::inflate, ::WelcomeScreenRunner + ) +} diff --git a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoEditScreen.kt b/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoEditScreen.kt index 84523531bd..0fc973c324 100644 --- a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoEditScreen.kt +++ b/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoEditScreen.kt @@ -2,8 +2,12 @@ package workflow.tutorial import com.squareup.workflow1.ui.AndroidScreen import com.squareup.workflow1.ui.ScreenViewFactory +import com.squareup.workflow1.ui.ScreenViewRunner import com.squareup.workflow1.ui.TextController +import com.squareup.workflow1.ui.ViewEnvironment import com.squareup.workflow1.ui.WorkflowUiExperimentalApi +import com.squareup.workflow1.ui.control +import com.squareup.workflow1.ui.navigation.setBackHandler import workflow.tutorial.views.databinding.TodoEditViewBinding @OptIn(WorkflowUiExperimentalApi::class) @@ -19,3 +23,19 @@ data class TodoEditScreen( override val viewFactory = ScreenViewFactory.fromViewBinding(TodoEditViewBinding::inflate, ::TodoEditScreenRunner) } + +@OptIn(WorkflowUiExperimentalApi::class) +private class TodoEditScreenRunner( + private val binding: TodoEditViewBinding +) : ScreenViewRunner { + + override fun showRendering( + rendering: TodoEditScreen, + environment: ViewEnvironment + ) { + binding.root.setBackHandler(rendering.onBackPressed) + binding.save.setOnClickListener { rendering.onSavePressed() } + rendering.title.control(binding.todoTitle) + rendering.note.control(binding.todoNote) + } +} diff --git a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoEditScreenRunner.kt b/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoEditScreenRunner.kt deleted file mode 100644 index e5796f9880..0000000000 --- a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoEditScreenRunner.kt +++ /dev/null @@ -1,24 +0,0 @@ -package workflow.tutorial - -import com.squareup.workflow1.ui.ScreenViewRunner -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.control -import com.squareup.workflow1.ui.navigation.setBackHandler -import workflow.tutorial.views.databinding.TodoEditViewBinding - -@OptIn(WorkflowUiExperimentalApi::class) -class TodoEditScreenRunner( - private val binding: TodoEditViewBinding -) : ScreenViewRunner { - - override fun showRendering( - rendering: TodoEditScreen, - environment: ViewEnvironment - ) { - binding.root.setBackHandler(rendering.onBackPressed) - binding.save.setOnClickListener { rendering.onSavePressed() } - rendering.title.control(binding.todoTitle) - rendering.note.control(binding.todoNote) - } -} diff --git a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoListScreen.kt b/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoListScreen.kt index 13a6644732..8b5cf9519f 100644 --- a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoListScreen.kt +++ b/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoListScreen.kt @@ -1,8 +1,13 @@ package workflow.tutorial +import androidx.recyclerview.widget.LinearLayoutManager import com.squareup.workflow1.ui.AndroidScreen import com.squareup.workflow1.ui.ScreenViewFactory +import com.squareup.workflow1.ui.ScreenViewRunner +import com.squareup.workflow1.ui.ViewEnvironment import com.squareup.workflow1.ui.WorkflowUiExperimentalApi +import com.squareup.workflow1.ui.navigation.setBackHandler +import workflow.tutorial.views.TodoListAdapter import workflow.tutorial.views.databinding.TodoListViewBinding /** @@ -22,3 +27,33 @@ data class TodoListScreen( override val viewFactory = ScreenViewFactory.fromViewBinding(TodoListViewBinding::inflate, ::TodoListScreenRunner) } + +@OptIn(WorkflowUiExperimentalApi::class) +private class TodoListScreenRunner( + private val todoListBinding: TodoListViewBinding +) : ScreenViewRunner { + + private val adapter = TodoListAdapter() + + init { + todoListBinding.todoList.layoutManager = LinearLayoutManager(todoListBinding.root.context) + todoListBinding.todoList.adapter = adapter + } + + override fun showRendering( + rendering: TodoListScreen, + environment: ViewEnvironment + ) { + todoListBinding.root.setBackHandler(rendering.onBackPressed) + todoListBinding.add.setOnClickListener { rendering.onAddPressed() } + + with(todoListBinding.todoListWelcome) { + text = + resources.getString(workflow.tutorial.views.R.string.todo_list_welcome, rendering.username) + } + + adapter.todoList = rendering.todoTitles + adapter.onTodoSelected = rendering.onRowPressed + adapter.notifyDataSetChanged() + } +} diff --git a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoListScreenRunner.kt b/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoListScreenRunner.kt deleted file mode 100644 index 35bd5b8a20..0000000000 --- a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/TodoListScreenRunner.kt +++ /dev/null @@ -1,39 +0,0 @@ -package workflow.tutorial - -import androidx.recyclerview.widget.LinearLayoutManager -import com.squareup.workflow1.ui.ScreenViewRunner -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.navigation.setBackHandler -import workflow.tutorial.views.TodoListAdapter -import workflow.tutorial.views.databinding.TodoListViewBinding - -@OptIn(WorkflowUiExperimentalApi::class) -class TodoListScreenRunner( - private val todoListBinding: TodoListViewBinding -) : ScreenViewRunner { - - private val adapter = TodoListAdapter() - - init { - todoListBinding.todoList.layoutManager = LinearLayoutManager(todoListBinding.root.context) - todoListBinding.todoList.adapter = adapter - } - - override fun showRendering( - rendering: TodoListScreen, - environment: ViewEnvironment - ) { - todoListBinding.root.setBackHandler(rendering.onBackPressed) - todoListBinding.add.setOnClickListener { rendering.onAddPressed() } - - with(todoListBinding.todoListWelcome) { - text = - resources.getString(workflow.tutorial.views.R.string.todo_list_welcome, rendering.username) - } - - adapter.todoList = rendering.todoTitles - adapter.onTodoSelected = rendering.onRowPressed - adapter.notifyDataSetChanged() - } -} diff --git a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/WelcomeScreen.kt b/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/WelcomeScreen.kt index 42d759826f..50a569040d 100644 --- a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/WelcomeScreen.kt +++ b/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/WelcomeScreen.kt @@ -2,8 +2,12 @@ package workflow.tutorial import com.squareup.workflow1.ui.AndroidScreen import com.squareup.workflow1.ui.ScreenViewFactory +import com.squareup.workflow1.ui.ScreenViewFactory.Companion.fromViewBinding +import com.squareup.workflow1.ui.ScreenViewRunner import com.squareup.workflow1.ui.TextController +import com.squareup.workflow1.ui.ViewEnvironment import com.squareup.workflow1.ui.WorkflowUiExperimentalApi +import com.squareup.workflow1.ui.control import workflow.tutorial.views.databinding.WelcomeViewBinding @OptIn(WorkflowUiExperimentalApi::class) @@ -13,6 +17,23 @@ data class WelcomeScreen( /** Callback when the login button is tapped. */ val onLogInPressed: () -> Unit ) : AndroidScreen { - override val viewFactory = - ScreenViewFactory.fromViewBinding(WelcomeViewBinding::inflate, ::WelcomeScreenRunner) + + override val viewFactory: ScreenViewFactory = + fromViewBinding(WelcomeViewBinding::inflate, ::WelcomeScreenRunner) +} + +@OptIn(WorkflowUiExperimentalApi::class) +private class WelcomeScreenRunner( + private val viewBinding: WelcomeViewBinding +) : ScreenViewRunner { + + override fun showRendering( + rendering: WelcomeScreen, + environment: ViewEnvironment + ) { + // TextController is a helper provided by the workflow library that takes + // care of the complexity of correctly interacting with EditTexts in a declarative manner. + rendering.username.control(viewBinding.username) + viewBinding.login.setOnClickListener { rendering.onLogInPressed() } + } } diff --git a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/WelcomeScreenRunner.kt b/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/WelcomeScreenRunner.kt deleted file mode 100644 index 165e55677a..0000000000 --- a/samples/tutorial/tutorial-final/src/main/java/workflow/tutorial/WelcomeScreenRunner.kt +++ /dev/null @@ -1,23 +0,0 @@ -package workflow.tutorial - -import com.squareup.workflow1.ui.ScreenViewRunner -import com.squareup.workflow1.ui.ViewEnvironment -import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.control -import workflow.tutorial.views.databinding.WelcomeViewBinding - -@OptIn(WorkflowUiExperimentalApi::class) -class WelcomeScreenRunner( - private val welcomeBinding: WelcomeViewBinding -) : ScreenViewRunner { - - override fun showRendering( - rendering: WelcomeScreen, - environment: ViewEnvironment - ) { - // TextController is a helper provided by the workflow library that takes - // care of the complexity of correctly interacting with EditTexts in a declarative manner. - rendering.username.control(welcomeBinding.username) - welcomeBinding.login.setOnClickListener { rendering.onLogInPressed() } - } -} diff --git a/workflow-file-templates.zip b/workflow-file-templates.zip new file mode 100644 index 0000000000..841226a569 Binary files /dev/null and b/workflow-file-templates.zip differ