Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import com.squareup.workflow1.config.AndroidRuntimeConfigTools
import com.squareup.workflow1.ui.Screen
import com.squareup.workflow1.ui.WorkflowLayout
import com.squareup.workflow1.ui.renderWorkflowIn
import com.squareup.workflow1.ui.unwrap
import com.squareup.workflow1.ui.withRegistry
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import timber.log.Timber

private val viewRegistry = SampleContainers
Expand All @@ -44,13 +46,15 @@ class PoetryActivity : AppCompatActivity() {
}

class PoetryModel(savedState: SavedStateHandle) : ViewModel() {
val renderings: StateFlow<Screen> by lazy {
val renderings: Flow<Screen> by lazy {
renderWorkflowIn(
workflow = RealPoemsBrowserWorkflow(RealPoemWorkflow()),
scope = viewModelScope,
prop = 0 to 0 to Poem.allPoems,
savedStateHandle = savedState,
runtimeConfig = AndroidRuntimeConfigTools.getAppWorkflowRuntimeConfig()
)
).onEach {
Timber.i("Navigated to %s", it.unwrap())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ import com.squareup.workflow1.config.AndroidRuntimeConfigTools
import com.squareup.workflow1.ui.Screen
import com.squareup.workflow1.ui.WorkflowLayout
import com.squareup.workflow1.ui.renderWorkflowIn
import com.squareup.workflow1.ui.unwrap
import com.squareup.workflow1.ui.withRegistry
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber

Expand Down Expand Up @@ -53,7 +55,7 @@ class RavenActivity : AppCompatActivity() {
class RavenModel(savedState: SavedStateHandle) : ViewModel() {
private val running = Job()

val renderings: StateFlow<Screen> by lazy {
val renderings: Flow<Screen> by lazy {
renderWorkflowIn(
workflow = RealPoemWorkflow(),
scope = viewModelScope,
Expand All @@ -62,6 +64,8 @@ class RavenModel(savedState: SavedStateHandle) : ViewModel() {
runtimeConfig = AndroidRuntimeConfigTools.getAppWorkflowRuntimeConfig()
) {
running.complete()
}.onEach {
Timber.i("Navigated to %s", it.unwrap())
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.squareup.sample.container.overviewdetail

import com.squareup.workflow1.ui.Screen
import com.squareup.workflow1.ui.Unwrappable
import com.squareup.workflow1.ui.navigation.BackStackScreen
import com.squareup.workflow1.ui.navigation.plus

Expand All @@ -19,7 +20,7 @@ class OverviewDetailScreen<out T : Screen> private constructor(
val overviewRendering: BackStackScreen<T>,
val detailRendering: BackStackScreen<T>? = null,
val selectDefault: (() -> Unit)? = null
) : Screen {
) : Screen, Unwrappable {
constructor(
overviewRendering: BackStackScreen<T>,
detailRendering: BackStackScreen<T>
Expand All @@ -37,6 +38,12 @@ class OverviewDetailScreen<out T : Screen> private constructor(
operator fun component1(): BackStackScreen<T> = overviewRendering
operator fun component2(): BackStackScreen<T>? = detailRendering

/**
* For nicer logging. See the call to [unwrap][com.squareup.workflow1.ui.unwrap]
* in the activity.
*/
override val unwrapped = detailRendering ?: overviewRendering

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.squareup.workflow1.ui.AndroidScreen
import com.squareup.workflow1.ui.Screen
import com.squareup.workflow1.ui.ScreenViewFactory
import com.squareup.workflow1.ui.ScreenViewFactory.Companion.map
import com.squareup.workflow1.ui.Unwrappable
import com.squareup.workflow1.ui.navigation.AlertOverlay
import com.squareup.workflow1.ui.navigation.AlertOverlay.Button.NEGATIVE
import com.squareup.workflow1.ui.navigation.AlertOverlay.Button.POSITIVE
Expand All @@ -37,12 +38,19 @@ object AreYouSureWorkflow :
): State = snapshot?.toParcelable() ?: Running

class Rendering(
val base: Screen,
val alert: AlertOverlay? = null
) : AndroidScreen<Rendering> {
private val base: Screen,
private val alert: AlertOverlay? = null
) : AndroidScreen<Rendering>, Unwrappable {
override val viewFactory: ScreenViewFactory<Rendering> = map { newRendering ->
BodyAndOverlaysScreen(newRendering.base, listOfNotNull(newRendering.alert))
}

/**
* For nicer logging. See the call to [unwrap][com.squareup.workflow1.ui.unwrap]
* in [HelloBackButtonActivity].
*/
override val unwrapped: Any
get() = alert ?: base
}

@Parcelize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ import com.squareup.workflow1.config.AndroidRuntimeConfigTools
import com.squareup.workflow1.ui.Screen
import com.squareup.workflow1.ui.WorkflowLayout
import com.squareup.workflow1.ui.renderWorkflowIn
import com.squareup.workflow1.ui.unwrap
import com.squareup.workflow1.ui.withRegistry
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber

private val viewRegistry = SampleContainers

Expand All @@ -39,12 +42,18 @@ class HelloBackButtonActivity : AppCompatActivity() {
finish()
}
}

companion object {
init {
Timber.plant(Timber.DebugTree())
}
}
}

class HelloBackButtonModel(savedState: SavedStateHandle) : ViewModel() {
private val running = Job()

val renderings: StateFlow<Screen> by lazy {
val renderings: Flow<Screen> by lazy {
renderWorkflowIn(
workflow = AreYouSureWorkflow,
scope = viewModelScope,
Expand All @@ -54,6 +63,8 @@ class HelloBackButtonModel(savedState: SavedStateHandle) : ViewModel() {
// This workflow handles the back button itself, so the activity can't.
// Instead, the workflow emits an output to signal that it's time to shut things down.
running.complete()
}.onEach {
Timber.i("Navigated to %s", it.unwrap())
}
}

Expand Down
1 change: 1 addition & 0 deletions workflow-ui/core-android/api/core-android.api
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ public final class com/squareup/workflow1/ui/navigation/BackButtonScreen : com/s
public synthetic fun getContent ()Ljava/lang/Object;
public final fun getOnBackPressed ()Lkotlin/jvm/functions/Function0;
public final fun getShadow ()Z
public fun getUnwrapped ()Ljava/lang/Object;
public fun getViewFactory ()Lcom/squareup/workflow1/ui/ScreenViewFactory;
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;
Expand Down
32 changes: 30 additions & 2 deletions workflow-ui/core-common/api/core-common.api
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,27 @@ public final class com/squareup/workflow1/ui/CompatibleKt {
public static final fun compatible (Ljava/lang/Object;Ljava/lang/Object;)Z
}

public abstract interface class com/squareup/workflow1/ui/Container {
public abstract interface class com/squareup/workflow1/ui/Composite : com/squareup/workflow1/ui/Unwrappable {
public abstract fun asSequence ()Lkotlin/sequences/Sequence;
public abstract fun getUnwrapped ()Ljava/lang/Object;
}

public final class com/squareup/workflow1/ui/Composite$DefaultImpls {
public static fun getUnwrapped (Lcom/squareup/workflow1/ui/Composite;)Ljava/lang/Object;
}

public abstract interface class com/squareup/workflow1/ui/Container : com/squareup/workflow1/ui/Composite {
public abstract fun map (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/Container;
}

public final class com/squareup/workflow1/ui/Container$DefaultImpls {
public static fun getUnwrapped (Lcom/squareup/workflow1/ui/Container;)Ljava/lang/Object;
}

public final class com/squareup/workflow1/ui/ContainerKt {
public static final fun unwrap (Ljava/lang/Object;)Ljava/lang/Object;
}

public final class com/squareup/workflow1/ui/EnvironmentScreen : com/squareup/workflow1/ui/Screen, com/squareup/workflow1/ui/Wrapper {
public fun <init> (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;)V
public synthetic fun <init> (Lcom/squareup/workflow1/ui/Screen;Lcom/squareup/workflow1/ui/ViewEnvironment;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand All @@ -25,6 +41,7 @@ public final class com/squareup/workflow1/ui/EnvironmentScreen : com/squareup/wo
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 fun getUnwrapped ()Ljava/lang/Object;
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;
Expand All @@ -49,6 +66,7 @@ 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 fun getUnwrapped ()Ljava/lang/Object;
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;
Expand All @@ -70,6 +88,10 @@ public final class com/squareup/workflow1/ui/TextControllerKt {
public static synthetic fun TextController$default (Ljava/lang/String;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/TextController;
}

public abstract interface class com/squareup/workflow1/ui/Unwrappable {
public abstract fun getUnwrapped ()Ljava/lang/Object;
}

public final class com/squareup/workflow1/ui/ViewEnvironment {
public static final field Companion Lcom/squareup/workflow1/ui/ViewEnvironment$Companion;
public fun equals (Ljava/lang/Object;)Z
Expand Down Expand Up @@ -138,6 +160,7 @@ public abstract interface class com/squareup/workflow1/ui/Wrapper : com/squareup
public final class com/squareup/workflow1/ui/Wrapper$DefaultImpls {
public static fun asSequence (Lcom/squareup/workflow1/ui/Wrapper;)Lkotlin/sequences/Sequence;
public static fun getCompatibilityKey (Lcom/squareup/workflow1/ui/Wrapper;)Ljava/lang/String;
public static fun getUnwrapped (Lcom/squareup/workflow1/ui/Wrapper;)Ljava/lang/Object;
}

public final class com/squareup/workflow1/ui/navigation/AlertOverlay : com/squareup/workflow1/ui/navigation/ModalOverlay {
Expand Down Expand Up @@ -218,6 +241,7 @@ public final class com/squareup/workflow1/ui/navigation/BackStackScreen : com/sq
public final fun getFrames ()Ljava/util/List;
public final fun getName ()Ljava/lang/String;
public final fun getTop ()Lcom/squareup/workflow1/ui/Screen;
public fun getUnwrapped ()Ljava/lang/Object;
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;
Expand All @@ -241,13 +265,15 @@ public final class com/squareup/workflow1/ui/navigation/BackStackScreenKt {
public static synthetic fun toBackStackScreenOrNull$default (Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Lcom/squareup/workflow1/ui/navigation/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/navigation/BodyAndOverlaysScreen : com/squareup/workflow1/ui/Compatible, com/squareup/workflow1/ui/Composite, com/squareup/workflow1/ui/Screen {
public fun <init> (Lcom/squareup/workflow1/ui/Screen;Ljava/util/List;Ljava/lang/String;)V
public synthetic fun <init> (Lcom/squareup/workflow1/ui/Screen;Ljava/util/List;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun asSequence ()Lkotlin/sequences/Sequence;
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 fun getUnwrapped ()Ljava/lang/Object;
public final fun mapBody (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/BodyAndOverlaysScreen;
public final fun mapOverlays (Lkotlin/jvm/functions/Function1;)Lcom/squareup/workflow1/ui/navigation/BodyAndOverlaysScreen;
}
Expand All @@ -258,6 +284,7 @@ public final class com/squareup/workflow1/ui/navigation/FullScreenModal : com/sq
public fun getCompatibilityKey ()Ljava/lang/String;
public fun getContent ()Lcom/squareup/workflow1/ui/Screen;
public synthetic fun getContent ()Ljava/lang/Object;
public fun getUnwrapped ()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;
Expand All @@ -278,5 +305,6 @@ public abstract interface class com/squareup/workflow1/ui/navigation/ScreenOverl
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 static fun getUnwrapped (Lcom/squareup/workflow1/ui/navigation/ScreenOverlay;)Ljava/lang/Object;
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,55 @@ package com.squareup.workflow1.ui
import com.squareup.workflow1.ui.Compatible.Companion.keyFor

/**
* A rendering type comprised of a set of other renderings.
* A rendering that wraps another that is actually the interesting
* bit (read: the visible bit), particularly from a logging or testing
* point of view.
*
* Why two parameter types? The separate [BaseT] type allows implementations
* This is the easiest way to customize behavior of the [unwrap] function.
*/
public interface Unwrappable {
/** Topmost wrapped content, or `this` if empty. */
public val unwrapped: Any
}

/**
* Handy for logging and testing, extracts the "topmost" bit from a receiving
* workflow rendering, honoring [Unwrappable] if applicable.
*/
public tailrec fun Any.unwrap(): Any {
if (this !is Unwrappable) return this
return unwrapped.unwrap()
}

/**
* A rendering that can be decomposed to a [sequence][asSequence] of others.
*/
public interface Composite<out T> : Unwrappable {
public fun asSequence(): Sequence<T>

public override val unwrapped: Any get() = asSequence().lastOrNull() ?: this
}

/**
* A structured [Composite] rendering comprised of a set of other
* renderings of a [specific type][C] of a particular [category][CategoryT],
* and whose contents can be transformed by [map].
*
* Why two parameter types? The separate [CategoryT] type allows implementations
* and sub-interfaces to constrain the types that [map] is allowed to
* transform [C] to. E.g., it allows `FooWrapper<S: Screen>` to declare
* transform [C] to. E.g., it allows `BunchOfScreens<S: Screen>` to declare
* that [map] is only able to transform `S` to other types of `Screen`.
*
* @param BaseT the invariant base type of the contents of such a container,
* @param CategoryT the invariant base type of the contents of such a container,
* usually [Screen] or [Overlay][com.squareup.workflow1.ui.navigation.Overlay].
* It is common for the [Container] itself to implement [BaseT], but that is
* It is common for the [Container] itself to implement [CategoryT], but that is
* not a requirement. E.g., [ScreenOverlay][com.squareup.workflow1.ui.navigation.ScreenOverlay]
* is an [Overlay][com.squareup.workflow1.ui.navigation.Overlay], but it
* wraps a [Screen].
*
* @param C the specific subtype of [BaseT] collected by this [Container].
* @param C the specific subtype of [CategoryT] collected by this [Container].
*/
public interface Container<BaseT, out C : BaseT> {
public fun asSequence(): Sequence<C>

public interface Container<CategoryT, out C : CategoryT> : Composite<C> {
/**
* Returns a [Container] with the [transform]ed contents of the receiver.
* It is expected that an implementation will take advantage of covariance
Expand All @@ -41,7 +71,7 @@ public interface Container<BaseT, out C : BaseT> {
* val childBackStackScreen = renderChild(childWorkflow) { ... }
* val loggingBackStackScreen = childBackStackScreen.map { LoggingScreen(it) }
*/
public fun <D : BaseT> map(transform: (C) -> D): Container<BaseT, D>
public fun <D : CategoryT> map(transform: (C) -> D): Container<CategoryT, D>
}

/**
Expand All @@ -50,9 +80,9 @@ public interface Container<BaseT, out C : BaseT> {
* [EnvironmentScreen][com.squareup.workflow1.ui.EnvironmentScreen] that allows
* changes to be made to the [ViewEnvironment].
*
* Usually a [Wrapper] is [Compatible] only with others of the same type with
* [Compatible] [content]. In aid of that, this interface extends [Compatible] and
* provides a convenient default implementation of [compatibilityKey].
* Usually a [Wrapper] is [Compatible] only with others that are of the same type
* and which are holding [Compatible] [content]. In aid of that, this interface extends
* [Compatible] and provides a convenient default implementation of [compatibilityKey].
*/
public interface Wrapper<BaseT : Any, out C : BaseT> : Container<BaseT, C>, Compatible {
public val content: C
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.squareup.workflow1.ui.navigation

import com.squareup.workflow1.ui.Compatible
import com.squareup.workflow1.ui.Compatible.Companion.keyFor
import com.squareup.workflow1.ui.Composite
import com.squareup.workflow1.ui.Screen

/**
Expand Down Expand Up @@ -75,9 +76,11 @@ public class BodyAndOverlaysScreen<B : Screen, O : Overlay>(
public val body: B,
public val overlays: List<O> = emptyList(),
public val name: String = ""
) : Screen, Compatible {
) : Screen, Compatible, Composite<Any> {
override val compatibilityKey: String = keyFor(this, name)

override fun asSequence(): Sequence<Any> = sequenceOf(body) + overlays.asSequence()

public fun <S : Screen> mapBody(transform: (B) -> S): BodyAndOverlaysScreen<S, O> {
return BodyAndOverlaysScreen(transform(body), overlays, name)
}
Expand Down
Loading
Loading