Skip to content

Commit c1df8c5

Browse files
Move SnapshotParcels.kt to workflow-core.
This gives workflow-core an android source set and moves the Parcelable-related Snapshot helpers to it. This makes more sense since the other Snapshot helpers are in the core module, and Parcelable isn't a UI thing it's just an android thing. This also paves the way for go/compose-based-workflow to use these helpers to support `rememberSaveable`.
1 parent fc9e379 commit c1df8c5

File tree

12 files changed

+189
-44
lines changed

12 files changed

+189
-44
lines changed

samples/containers/hello-back-button/src/main/java/com/squareup/sample/hellobackbutton/AreYouSureWorkflow.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import com.squareup.workflow1.Snapshot
1010
import com.squareup.workflow1.StatefulWorkflow
1111
import com.squareup.workflow1.WorkflowAction.Companion.noAction
1212
import com.squareup.workflow1.action
13+
import com.squareup.workflow1.toParcelable
14+
import com.squareup.workflow1.toSnapshot
1315
import com.squareup.workflow1.ui.AndroidScreen
1416
import com.squareup.workflow1.ui.Screen
1517
import com.squareup.workflow1.ui.ScreenViewFactory
@@ -22,8 +24,6 @@ import com.squareup.workflow1.ui.navigation.AlertOverlay.Event.ButtonClicked
2224
import com.squareup.workflow1.ui.navigation.AlertOverlay.Event.Canceled
2325
import com.squareup.workflow1.ui.navigation.BackButtonScreen
2426
import com.squareup.workflow1.ui.navigation.BodyAndOverlaysScreen
25-
import com.squareup.workflow1.ui.toParcelable
26-
import com.squareup.workflow1.ui.toSnapshot
2727
import kotlinx.parcelize.Parcelize
2828

2929
/**

samples/containers/hello-back-button/src/main/java/com/squareup/sample/hellobackbutton/HelloBackButtonWorkflow.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import com.squareup.sample.hellobackbutton.HelloBackButtonWorkflow.State.Baker
77
import com.squareup.sample.hellobackbutton.HelloBackButtonWorkflow.State.Charlie
88
import com.squareup.workflow1.Snapshot
99
import com.squareup.workflow1.StatefulWorkflow
10-
import com.squareup.workflow1.ui.toParcelable
11-
import com.squareup.workflow1.ui.toSnapshot
10+
import com.squareup.workflow1.toParcelable
11+
import com.squareup.workflow1.toSnapshot
1212
import kotlinx.parcelize.Parcelize
1313

1414
object HelloBackButtonWorkflow : StatefulWorkflow<Unit, State, Nothing, HelloBackButtonScreen>() {

workflow-core/api/workflow-core.api renamed to workflow-core/api/jvm/workflow-core.api

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ public final class com/squareup/workflow1/WorkflowIdentifier$Companion {
386386
public final fun parse (Lokio/ByteString;)Lcom/squareup/workflow1/WorkflowIdentifier;
387387
}
388388

389-
public final class com/squareup/workflow1/WorkflowIdentifierExKt {
389+
public final class com/squareup/workflow1/WorkflowIdentifierEx_jvmKt {
390390
public static final fun getWorkflowIdentifier (Lkotlin/reflect/KClass;)Lcom/squareup/workflow1/WorkflowIdentifier;
391391
}
392392

workflow-core/build.gradle.kts

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import com.squareup.workflow1.buildsrc.iosWithSimulatorArm64
22

33
plugins {
44
id("kotlin-multiplatform")
5+
id("com.android.kotlin.multiplatform.library")
56
id("published")
67
}
78

@@ -13,18 +14,63 @@ kotlin {
1314
if (targets == "kmp" || targets == "jvm") {
1415
jvm { withJava() }
1516
}
17+
// The default KMP
18+
// ["hierarchy template"](https://www.jetbrains.com/help/kotlin-multiplatform-dev/multiplatform-hierarchy.html#see-the-full-hierarchy-template)
19+
// configures `androidMain` and `jvmMain` to be entirely separate targets, even though Android
20+
// *can* be made to be a child of JVM. Changing this requires completely wiring up all targets
21+
// ourselves though, so for now we've left them separate to simplify gradle config. If there ends
22+
// up being too much code duplication, we can either make `androidMain` a child of `jvmMain`, or
23+
// introduce a new shared target that includes both of them. Compose, for example, uses a
24+
// structure where `jvm` is the shared parent of both `android` and `desktop`.
25+
if (targets == "kmp" || targets == "android") {
26+
androidLibrary {
27+
namespace = "com.squareup.workflow1.android"
28+
testNamespace = "$namespace.test"
29+
30+
compileSdk = libs.versions.compileSdk.get().toInt()
31+
minSdk = libs.versions.minSdk.get().toInt()
32+
33+
withHostTestBuilder {
34+
}.configure {
35+
}
36+
37+
withDeviceTestBuilder {
38+
sourceSetTreeName = "test"
39+
}.configure {
40+
instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
41+
42+
// Disable transition and rotation animations.
43+
animationsDisabled = true
44+
}
45+
}
46+
}
1647
if (targets == "kmp" || targets == "js") {
1748
js(IR) { browser() }
1849
}
19-
}
2050

21-
dependencies {
22-
commonMainApi(libs.kotlin.jdk6)
23-
commonMainApi(libs.kotlinx.coroutines.core)
24-
// For Snapshot.
25-
commonMainApi(libs.squareup.okio)
51+
sourceSets {
52+
commonMain {
53+
dependencies {
54+
api(libs.kotlin.jdk6)
55+
api(libs.kotlinx.coroutines.core)
56+
// For Snapshot.
57+
api(libs.squareup.okio)
58+
}
59+
}
2660

27-
commonTestImplementation(libs.kotlinx.atomicfu)
28-
commonTestImplementation(libs.kotlinx.coroutines.test.common)
29-
commonTestImplementation(libs.kotlin.test.core)
61+
commonTest {
62+
dependencies {
63+
implementation(libs.kotlinx.atomicfu)
64+
implementation(libs.kotlinx.coroutines.test.common)
65+
implementation(libs.kotlin.test.core)
66+
}
67+
}
68+
69+
getByName("androidHostTest") {
70+
dependencies {
71+
implementation(libs.robolectric)
72+
implementation(libs.robolectric.annotations)
73+
}
74+
}
75+
}
3076
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
com.squareup.okio:okio-jvm:3.3.0
2+
com.squareup.okio:okio:3.3.0
3+
org.jetbrains.kotlin:kotlin-bom:2.0.21
4+
org.jetbrains.kotlin:kotlin-stdlib-common:2.0.21
5+
org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.21
6+
org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21
7+
org.jetbrains.kotlin:kotlin-stdlib:2.0.21
8+
org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3
9+
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3
10+
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3
11+
org.jetbrains:annotations:23.0.0
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.squareup.workflow1
2+
3+
import android.os.Bundle
4+
import org.junit.runner.RunWith
5+
import org.robolectric.RobolectricTestRunner
6+
import kotlin.test.Test
7+
import kotlin.test.assertEquals
8+
import kotlin.test.assertNotNull
9+
import kotlin.test.assertTrue
10+
11+
@RunWith(RobolectricTestRunner::class)
12+
class SnapshotParcelsTest {
13+
14+
@Test fun parcelableToSnapshot_savesAndRestores() {
15+
val snapshot = Bundle().apply {
16+
putString("key", "value")
17+
}.toSnapshot()
18+
val restored = snapshot.toParcelable<Bundle>()
19+
20+
assertNotNull(restored)
21+
assertTrue(restored.containsKey("key"))
22+
assertEquals("value", restored.getString("key"))
23+
}
24+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.squareup.workflow1
2+
3+
import android.os.Build.VERSION
4+
import android.os.Build.VERSION_CODES
5+
import android.os.Parcel
6+
import android.os.Parcelable
7+
import okio.ByteString
8+
9+
/**
10+
* Wraps receiver in a [Snapshot] suitable for use with [StatefulWorkflow].
11+
* Intended to allow use of `@Parcelize`.
12+
*
13+
* Read the [Parcelable] back with [toParcelable].
14+
*/
15+
public fun Parcelable.toSnapshot(): Snapshot = Snapshot.write { bufferedSink ->
16+
val parcel = Parcel.obtain()
17+
parcel.writeParcelable(this, 0)
18+
val byteArray = parcel.marshall()
19+
bufferedSink.write(byteArray)
20+
parcel.recycle()
21+
}
22+
23+
/**
24+
* Returns a [Parcelable] previously wrapped with [toSnapshot], or `null` if the receiver is empty.
25+
*/
26+
public inline fun <reified T : Parcelable> Snapshot.toParcelable(): T? =
27+
bytes.toParcelable<T>()
28+
29+
public inline fun <reified T : Parcelable> ByteString.toParcelable(): T? =
30+
toParcelable(T::class.java)
31+
32+
@PublishedApi
33+
internal fun <T : Parcelable> ByteString.toParcelable(targetClass: Class<T>): T? {
34+
if (size == 0) return null
35+
36+
val parcel = Parcel.obtain()
37+
val byteArray = toByteArray()
38+
parcel.unmarshall(byteArray, 0, byteArray.size)
39+
parcel.setDataPosition(0)
40+
val rtn = if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
41+
parcel.readParcelable(Snapshot::class.java.classLoader, targetClass)!!
42+
} else {
43+
@Suppress("DEPRECATION")
44+
parcel.readParcelable(Snapshot::class.java.classLoader)!!
45+
}
46+
parcel.recycle()
47+
return rtn
48+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.squareup.workflow1
2+
3+
import kotlin.reflect.KClass
4+
5+
internal actual fun commonUniqueClassName(kClass: KClass<*>): String {
6+
return kClass.qualifiedName ?: kClass.toString()
7+
}

0 commit comments

Comments
 (0)