Skip to content

Commit e2f5366

Browse files
committed
Backports BackStackContainer view persistence fix
The fix in #730 should have been made on main.
1 parent 70cc79b commit e2f5366

File tree

5 files changed

+69
-11
lines changed

5 files changed

+69
-11
lines changed

workflow-ui/container-android/api/container-android.api

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ public final class com/squareup/workflow1/ui/backstack/BackStackContainer$SavedS
4242
}
4343

4444
public final class com/squareup/workflow1/ui/backstack/BackStackContainer$SavedState$CREATOR : android/os/Parcelable$Creator {
45-
public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/backstack/ViewStateCache$Saved;
45+
public fun createFromParcel (Landroid/os/Parcel;)Lcom/squareup/workflow1/ui/backstack/BackStackContainer$SavedState;
4646
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
47-
public fun newArray (I)[Lcom/squareup/workflow1/ui/backstack/ViewStateCache$Saved;
47+
public fun newArray (I)[Lcom/squareup/workflow1/ui/backstack/BackStackContainer$SavedState;
4848
public synthetic fun newArray (I)[Ljava/lang/Object;
4949
}
5050

workflow-ui/container-android/src/androidTest/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
xmlns:android="http://schemas.android.com/apk/res/android">
33

44
<application>
5+
<activity android:name="androidx.activity.ComponentActivity"/>
56
<activity
67
android:name="com.squareup.workflow1.ui.modal.test.ModalViewContainerLifecycleActivity"
78
android:theme="@style/Theme.AppCompat.NoActionBar"/>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ import org.junit.rules.RuleChain
2525

2626
/**
2727
* Uses a custom subclass, [NoTransitionBackStackContainer], to ensure transitions
28-
* are synchronus.
28+
* are synchronous.
2929
*/
3030
@OptIn(WorkflowUiExperimentalApi::class)
31-
internal class BackStackContainerPersistenceTest {
31+
internal class BackStackContainerPersistenceLifecycleTest {
3232

3333
private val scenarioRule =
3434
ActivityScenarioRule(BackStackContainerLifecycleActivity::class.java)
Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
package com.squareup.sample.container.overviewdetail
1+
package com.squareup.workflow1.ui.backstack.test
22

33
import android.content.Context
4+
import android.os.Parcel
5+
import android.os.Parcelable
6+
import android.text.Editable
7+
import android.util.SparseArray
48
import android.view.View
9+
import android.widget.EditText
510
import androidx.activity.ComponentActivity
611
import androidx.test.ext.junit.rules.ActivityScenarioRule
712
import com.google.common.truth.Truth.assertThat
@@ -28,10 +33,59 @@ internal class BackStackContainerTest {
2833
override val compatibilityKey = name
2934
override val viewFactory: ViewFactory<Rendering>
3035
get() = BuilderViewFactory(Rendering::class) { r, e, ctx, _ ->
31-
View(ctx).also { it.bindShowRendering(r, e) { _, _ -> /* Noop */ } }
36+
EditText(ctx).apply {
37+
// Must have an id to participate in view persistence.
38+
id = 65
39+
bindShowRendering(r, e) { _, _ -> /* Noop */ }
40+
}
3241
}
3342
}
3443

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

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public open class BackStackContainer @JvmOverloads constructor(
187187
}
188188

189189
public constructor(source: Parcel) : super(source) {
190-
this.savedViewState = source.readParcelable(ViewStateCache.Saved::class.java.classLoader)!!
190+
savedViewState = source.readParcelable(ViewStateCache.Saved::class.java.classLoader)!!
191191
}
192192

193193
public val savedViewState: ViewStateCache.Saved
@@ -200,11 +200,10 @@ public open class BackStackContainer @JvmOverloads constructor(
200200
out.writeParcelable(savedViewState, flags)
201201
}
202202

203-
public companion object CREATOR : Creator<ViewStateCache.Saved> {
204-
override fun createFromParcel(source: Parcel): ViewStateCache.Saved =
205-
ViewStateCache.Saved(source)
203+
public companion object CREATOR : Creator<SavedState> {
204+
override fun createFromParcel(source: Parcel): SavedState = SavedState(source)
206205

207-
override fun newArray(size: Int): Array<ViewStateCache.Saved?> = arrayOfNulls(size)
206+
override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
208207
}
209208
}
210209

0 commit comments

Comments
 (0)