Skip to content

Commit 7801194

Browse files
authored
Merge pull request #730 from square/ray/oops-broken-parcelable-why-is-this-green
Fixes BackStackContainer view persistence
2 parents 49a624a + 5519cb4 commit 7801194

File tree

3 files changed

+73
-8
lines changed

3 files changed

+73
-8
lines changed

workflow-ui/core-android/api/core-android.api

+2-2
Original file line numberDiff line numberDiff line change
@@ -493,9 +493,9 @@ public final class com/squareup/workflow1/ui/container/BackStackContainer$SavedS
493493
}
494494

495495
public final class com/squareup/workflow1/ui/container/BackStackContainer$SavedState$CREATOR : android/os/Parcelable$Creator {
496-
public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/container/ViewStateCache$Saved;
496+
public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/container/BackStackContainer$SavedState;
497497
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
498-
public fun newArray (I)[Lcom/squareup/workflow1/ui/container/ViewStateCache$Saved;
498+
public fun newArray (I)[Lcom/squareup/workflow1/ui/container/BackStackContainer$SavedState;
499499
public synthetic fun newArray (I)[Ljava/lang/Object;
500500
}
501501

workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/BackStackContainerTest.kt

+67-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package com.squareup.workflow1.ui.container
22

33
import android.content.Context
4-
import android.view.View
4+
import android.os.Parcel
5+
import android.os.Parcelable
6+
import android.text.Editable
7+
import android.util.SparseArray
8+
import android.widget.EditText
59
import androidx.activity.ComponentActivity
610
import androidx.test.ext.junit.rules.ActivityScenarioRule
711
import com.google.common.truth.Truth
@@ -29,10 +33,67 @@ internal class BackStackContainerTest {
2933
override val compatibilityKey = name
3034
override val viewFactory: ScreenViewFactory<Rendering>
3135
get() = ScreenViewFactory.fromCode<Rendering> { _, initialRendering, context, _ ->
32-
ScreenViewHolder(initialRendering, View(context)) { _, _ -> /* Noop */ }
36+
ScreenViewHolder(
37+
initialRendering,
38+
EditText(context).apply {
39+
// Must have an id to participate in view persistence.
40+
id = 65
41+
}
42+
) { _, _ -> /* Noop */ }
3343
}
3444
}
3545

46+
@Test fun savedStateParcelingWorks() {
47+
scenario.onActivity { activity ->
48+
val originalView = VisibleBackStackContainer(activity).apply {
49+
// Must have an id to participate in view persistence.
50+
id = 42
51+
}
52+
val holder1 = ScreenViewHolder<BackStackScreen<*>>(EMPTY, originalView) { r, e ->
53+
originalView.update(r, e)
54+
}
55+
56+
// Show "able".
57+
holder1.show(BackStackScreen(Rendering("able")), EMPTY)
58+
// Type "first" into the rendered EditText.
59+
(originalView.getChildAt(0) as EditText).text = "first".toEditable()
60+
// Push "baker" on top of "able".
61+
holder1.show(BackStackScreen(Rendering("able"), Rendering("baker")), EMPTY)
62+
// Type "second" into the replacement rendered EditText.
63+
(originalView.getChildAt(0) as EditText).text = "second".toEditable()
64+
65+
// Save the view state to a ByteArray and read it out again, exercising all of
66+
// the Parcel machinery.
67+
val savedArray = SparseArray<Parcelable>()
68+
originalView.saveHierarchyState(savedArray)
69+
val bytes = Parcel.obtain().let { parcel ->
70+
parcel.writeSparseArray(savedArray)
71+
parcel.marshall().also { parcel.recycle() }
72+
}
73+
val restoredArray = Parcel.obtain().let { parcel ->
74+
parcel.unmarshall(bytes, 0, bytes.size)
75+
parcel.setDataPosition(0)
76+
parcel.readSparseArray<Parcelable>(this::class.java.classLoader)!!.also { parcel.recycle() }
77+
}
78+
79+
// Create a new BackStackContainer with the same id as the original
80+
val restoredView = VisibleBackStackContainer(activity).apply { id = 42 }
81+
val restoredHolder = ScreenViewHolder<BackStackScreen<*>>(EMPTY, restoredView) { r, e ->
82+
restoredView.update(r, e)
83+
}
84+
// Have it render the same able > baker back stack that we last showed in the original.
85+
restoredHolder.show(BackStackScreen(Rendering("able"), Rendering("baker")), EMPTY)
86+
// Restore the view hierarchy.
87+
restoredView.restoreHierarchyState(restoredArray)
88+
// Android took care of restoring the text that was last shown.
89+
assertThat((restoredView.getChildAt(0) as EditText).text.toString()).isEqualTo("second")
90+
// Pop back to able.
91+
restoredHolder.show(BackStackScreen(Rendering("able")), EMPTY)
92+
// BackStackContainer restored the text we had typed on that.
93+
assertThat((restoredView.getChildAt(0) as EditText).text.toString()).isEqualTo("first")
94+
}
95+
}
96+
3697
@Test fun firstScreenIsRendered() {
3798
scenario.onActivity { activity ->
3899
val view = VisibleBackStackContainer(activity)
@@ -116,3 +177,7 @@ internal class BackStackContainerTest {
116177
}
117178
}
118179
}
180+
181+
private fun String.toEditable(): Editable {
182+
return Editable.Factory.getInstance().newEditable(this)
183+
}

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,11 @@ public open class BackStackContainer @JvmOverloads constructor(
214214
out.writeParcelable(savedViewState, flags)
215215
}
216216

217-
public companion object CREATOR : Creator<Saved> {
218-
override fun createFromParcel(source: Parcel): ViewStateCache.Saved =
219-
ViewStateCache.Saved(source)
217+
public companion object CREATOR : Creator<SavedState> {
218+
override fun createFromParcel(source: Parcel): SavedState =
219+
SavedState(source)
220220

221-
override fun newArray(size: Int): Array<ViewStateCache.Saved?> = arrayOfNulls(size)
221+
override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
222222
}
223223
}
224224
}

0 commit comments

Comments
 (0)