diff --git a/workflow-ui/container-android/api/container-android.api b/workflow-ui/container-android/api/container-android.api index 3dccc32c1..9c5903478 100644 --- a/workflow-ui/container-android/api/container-android.api +++ b/workflow-ui/container-android/api/container-android.api @@ -42,9 +42,9 @@ public final class com/squareup/workflow1/ui/backstack/BackStackContainer$SavedS } public final class com/squareup/workflow1/ui/backstack/BackStackContainer$SavedState$CREATOR : android/os/Parcelable$Creator { - public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/backstack/ViewStateCache$Saved; + public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/backstack/BackStackContainer$SavedState; public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; - public fun newArray (I)[Lcom/squareup/workflow1/ui/backstack/ViewStateCache$Saved; + public fun newArray (I)[Lcom/squareup/workflow1/ui/backstack/BackStackContainer$SavedState; public synthetic fun newArray (I)[Ljava/lang/Object; } diff --git a/workflow-ui/container-android/src/androidTest/AndroidManifest.xml b/workflow-ui/container-android/src/androidTest/AndroidManifest.xml index 1f57b1602..fcdf46798 100644 --- a/workflow-ui/container-android/src/androidTest/AndroidManifest.xml +++ b/workflow-ui/container-android/src/androidTest/AndroidManifest.xml @@ -2,6 +2,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"> + diff --git a/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/backstack/test/BackStackContainerPersistenceTest.kt b/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/backstack/test/BackStackContainerPersistenceLifecycleTest.kt similarity index 99% rename from workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/backstack/test/BackStackContainerPersistenceTest.kt rename to workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/backstack/test/BackStackContainerPersistenceLifecycleTest.kt index 3feb436ac..96158425e 100644 --- a/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/backstack/test/BackStackContainerPersistenceTest.kt +++ b/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/backstack/test/BackStackContainerPersistenceLifecycleTest.kt @@ -25,10 +25,10 @@ import org.junit.rules.RuleChain /** * Uses a custom subclass, [NoTransitionBackStackContainer], to ensure transitions - * are synchronus. + * are synchronous. */ @OptIn(WorkflowUiExperimentalApi::class) -internal class BackStackContainerPersistenceTest { +internal class BackStackContainerPersistenceLifecycleTest { private val scenarioRule = ActivityScenarioRule(BackStackContainerLifecycleActivity::class.java) diff --git a/samples/containers/android/src/androidTest/java/com/squareup/sample/container/overviewdetail/BackStackContainerTest.kt b/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/backstack/test/BackStackContainerTest.kt similarity index 56% rename from samples/containers/android/src/androidTest/java/com/squareup/sample/container/overviewdetail/BackStackContainerTest.kt rename to workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/backstack/test/BackStackContainerTest.kt index 64604fd32..b7a968a78 100644 --- a/samples/containers/android/src/androidTest/java/com/squareup/sample/container/overviewdetail/BackStackContainerTest.kt +++ b/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/backstack/test/BackStackContainerTest.kt @@ -1,7 +1,12 @@ -package com.squareup.sample.container.overviewdetail +package com.squareup.workflow1.ui.backstack.test import android.content.Context +import android.os.Parcel +import android.os.Parcelable +import android.text.Editable +import android.util.SparseArray import android.view.View +import android.widget.EditText import androidx.activity.ComponentActivity import androidx.test.ext.junit.rules.ActivityScenarioRule import com.google.common.truth.Truth.assertThat @@ -28,10 +33,59 @@ internal class BackStackContainerTest { override val compatibilityKey = name override val viewFactory: ViewFactory get() = BuilderViewFactory(Rendering::class) { r, e, ctx, _ -> - View(ctx).also { it.bindShowRendering(r, e) { _, _ -> /* Noop */ } } + EditText(ctx).apply { + // Must have an id to participate in view persistence. + id = 65 + bindShowRendering(r, e) { _, _ -> /* Noop */ } + } } } + @Test fun savedStateParcelingWorks() { + scenario.onActivity { activity -> + val originalView = VisibleBackStackContainer(activity).apply { + // Must have an id to participate in view persistence. + id = 42 + } + + // Show "able". + originalView.show(BackStackScreen(Rendering("able"))) + // Type "first" into the rendered EditText. + (originalView.getChildAt(0) as EditText).text = "first".toEditable() + // Push "baker" on top of "able". + originalView.show(BackStackScreen(Rendering("able"), Rendering("baker"))) + // Type "second" into the replacement rendered EditText. + (originalView.getChildAt(0) as EditText).text = "second".toEditable() + + // Save the view state to a ByteArray and read it out again, exercising all of + // the Parcel machinery. + val savedArray = SparseArray() + originalView.saveHierarchyState(savedArray) + val bytes = Parcel.obtain().let { parcel -> + parcel.writeSparseArray(savedArray) + parcel.marshall().also { parcel.recycle() } + } + val restoredArray = Parcel.obtain().let { parcel -> + parcel.unmarshall(bytes, 0, bytes.size) + parcel.setDataPosition(0) + parcel.readSparseArray(this::class.java.classLoader)!!.also { parcel.recycle() } + } + + // Create a new BackStackContainer with the same id as the original + val restoredView = VisibleBackStackContainer(activity).apply { id = 42 } + // Have it render the same able > baker back stack that we last showed in the original. + restoredView.show(BackStackScreen(Rendering("able"), Rendering("baker"))) + // Restore the view hierarchy. + restoredView.restoreHierarchyState(restoredArray) + // Android took care of restoring the text that was last shown. + assertThat((restoredView.getChildAt(0) as EditText).text.toString()).isEqualTo("second") + // Pop back to able. + restoredView.show(BackStackScreen(Rendering("able"))) + // BackStackContainer restored the text we had typed on that. + assertThat((restoredView.getChildAt(0) as EditText).text.toString()).isEqualTo("first") + } + } + @Test fun firstScreenIsRendered() { scenario.onActivity { activity -> val c = VisibleBackStackContainer(activity) @@ -100,3 +154,7 @@ internal class BackStackContainerTest { } } } + +private fun String.toEditable(): Editable { + return Editable.Factory.getInstance().newEditable(this) +} diff --git a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/backstack/BackStackContainer.kt b/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/backstack/BackStackContainer.kt index 910a352ef..198ce88e3 100644 --- a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/backstack/BackStackContainer.kt +++ b/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/backstack/BackStackContainer.kt @@ -187,7 +187,7 @@ public open class BackStackContainer @JvmOverloads constructor( } public constructor(source: Parcel) : super(source) { - this.savedViewState = source.readParcelable(ViewStateCache.Saved::class.java.classLoader)!! + savedViewState = source.readParcelable(ViewStateCache.Saved::class.java.classLoader)!! } public val savedViewState: ViewStateCache.Saved @@ -200,11 +200,10 @@ public open class BackStackContainer @JvmOverloads constructor( out.writeParcelable(savedViewState, flags) } - public companion object CREATOR : Creator { - override fun createFromParcel(source: Parcel): ViewStateCache.Saved = - ViewStateCache.Saved(source) + public companion object CREATOR : Creator { + override fun createFromParcel(source: Parcel): SavedState = SavedState(source) - override fun newArray(size: Int): Array = arrayOfNulls(size) + override fun newArray(size: Int): Array = arrayOfNulls(size) } }