Skip to content

Commit

Permalink
Merge pull request #403 from square/ray/decor-397
Browse files Browse the repository at this point in the history
Tweaks to Compatible API, DecorativeViewFactory kdoc.
  • Loading branch information
rjrjr committed Apr 8, 2021
2 parents aaa6007 + 51d43ee commit c787931
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ import kotlin.reflect.KClass
* 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)
* class AliasRendering(val similarData: String)
* class AliasRendering(val similarData: String) : Compatible {
* override val compatibilityKey: String = Compatible.keyFor(wrapped)
* }
*
* object DecorativeViewFactory : ViewFactory<AliasRendering>
* by DecorativeViewFactory(
Expand All @@ -35,7 +41,9 @@ import kotlin.reflect.KClass
* class NeutronFlowPolarityOverride<W>(
* val wrapped: W,
* val polarity: NeutronFlowPolarity
* )
* ) : Compatible {
* override val compatibilityKey: String = Compatible.keyFor(wrapped)
* }
*
* object NeutronFlowPolarityViewFactory : ViewFactory<NeutronFlowPolarityOverride<*>>
* by DecorativeViewFactory(
Expand All @@ -47,7 +55,9 @@ import kotlin.reflect.KClass
*
* To make a decorator type that customizes [View] initialization:
*
* class WithTutorialTips<W>(val wrapped: W)
* class WithTutorialTips<W>(val wrapped: W) : Compatible {
* override val compatibilityKey: String = Compatible.keyFor(wrapped)
* }
*
* object WithTutorialTipsViewFactory : ViewFactory<WithTutorialTips<*>>
* by DecorativeViewFactory(
Expand All @@ -62,7 +72,9 @@ import kotlin.reflect.KClass
* val wrapped: W,
* val override: Boolean = false,
* val onBackPressed: (() -> Unit)? = null
* )
* ) : Compatible {
* override val compatibilityKey: String = Compatible.keyFor(wrapped)
* }
*
* object BackButtonViewFactory : ViewFactory<BackButtonScreen<*>>
* by DecorativeViewFactory(
Expand Down
6 changes: 6 additions & 0 deletions workflow-ui/core-common/api/core-common.api
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
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;
}

public final class com/squareup/workflow1/ui/Compatible$Companion {
public final fun keyFor (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String;
public static synthetic fun keyFor$default (Lcom/squareup/workflow1/ui/Compatible$Companion;Ljava/lang/Object;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String;
}

public final class com/squareup/workflow1/ui/CompatibleKt {
public static final fun compatible (Ljava/lang/Object;Ljava/lang/Object;)Z
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,17 @@ public interface Compatible {
* Instances of the same type are [compatible] iff they have the same [compatibilityKey].
*/
public val compatibilityKey: String

public companion object {
/**
* Calculates a suitable [Compatible.compatibilityKey] for a given [value] and [name].
*/
public fun keyFor(
value: Any,
name: String = ""
): String {
return ((value as? Compatible)?.compatibilityKey ?: value::class.java.name) +
if (name.isEmpty()) "" else "+$name"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,20 @@ public data class Named<W : Any>(
require(name.isNotBlank()) { "name must not be blank." }
}

override val compatibilityKey: String = keyFor(wrapped, name)
override val compatibilityKey: String = Compatible.keyFor(wrapped, name)

override fun toString(): String {
return "${super.toString()}: $compatibilityKey"
}

public companion object {
/**
* Calculates the [Named.compatibilityKey] for a given [value] and [name].
*/
@Deprecated(
"Use Compatible.keyFor",
ReplaceWith("Compatible.keyFor(value, name)", "com.squareup.workflow1.ui.Compatible")
)
public fun keyFor(
value: Any,
name: String = ""
): String {
return ((value as? Compatible)?.compatibilityKey ?: value::class.java.name) + "-Named($name)"
}
): String = Compatible.keyFor(value, name)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.junit.Test
// If you try to replace isTrue() with isTrue compilation fails.
@OptIn(WorkflowUiExperimentalApi::class)
@Suppress("UsePropertyAccessSyntax")
class NamedTest {
internal class NamedTest {
object Whut
object Hey

Expand Down Expand Up @@ -58,7 +58,7 @@ class NamedTest {

@Test fun `recursive keys are legible`() {
assertThat(Named(Named(Hey, "one"), "ho").compatibilityKey)
.isEqualTo("com.squareup.workflow1.ui.NamedTest\$Hey-Named(one)-Named(ho)")
.isEqualTo("com.squareup.workflow1.ui.NamedTest\$Hey+one+ho")
}

private class Foo(override val compatibilityKey: String) : Compatible
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import com.squareup.workflow1.ui.Named
import com.squareup.workflow1.ui.Compatible
import com.squareup.workflow1.ui.ViewEnvironment
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.WorkflowViewStub
Expand Down Expand Up @@ -147,11 +147,11 @@ public abstract class ModalContainer<ModalRenderingT : Any> @JvmOverloads constr
) {
internal fun save(): KeyAndBundle {
val saved = dialog.window!!.saveHierarchyState()
return KeyAndBundle(Named.keyFor(modalRendering), saved)
return KeyAndBundle(Compatible.keyFor(modalRendering), saved)
}

internal fun restore(keyAndBundle: KeyAndBundle) {
if (Named.keyFor(modalRendering) == keyAndBundle.compatibilityKey) {
if (Compatible.keyFor(modalRendering) == keyAndBundle.compatibilityKey) {
dialog.window!!.restoreHierarchyState(keyAndBundle.bundle)
}
}
Expand Down

0 comments on commit c787931

Please sign in to comment.