From 79354e33a51229b4d2ae3fed770754efeab21754 Mon Sep 17 00:00:00 2001 From: Zach Klippenstein Date: Wed, 16 Jul 2025 11:29:30 -0700 Subject: [PATCH] Move workflow-runtime-android into android source set in workflow-runtime module. This keeps all the runtime code in the same module, reducing the number of dependencies external code need to specify, and making it possible for our android-specific runtime code to directly access internal code in the runtime module, and satisfy more specific expect/actuals. It also lets us write android-specific tests for runtime code. Additionally, this helps pave the way for go/compose-based-workflows. --- artifacts.json | 18 ++--- settings.gradle.kts | 1 - .../dependencies/releaseRuntimeClasspath.txt | 56 +++++++++++++ workflow-runtime-android/README.md | 11 --- .../api/workflow-runtime-android.api | 10 --- workflow-runtime-android/build.gradle.kts | 35 -------- workflow-runtime-android/gradle.properties | 3 - workflow-runtime/README.md | 10 +-- .../api/{ => jvm}/workflow-runtime.api | 0 workflow-runtime/build.gradle.kts | 80 ++++++++++++++++--- .../dependencies/androidRuntimeClasspath.txt | 0 .../kotlin}/AndroidManifest.xml | 0 .../AndroidDispatchersRenderWorkflowInTest.kt | 0 .../android/AndroidRenderWorkflowInTest.kt | 0 workflow-runtime/src/androidMain/README.md | 10 +++ .../android/AndroidRenderWorkflow.kt | 0 .../workflow1/android/PickledTreesnapshot.kt | 0 .../workflow1/android/TreeSnapshotSaver.kt | 0 .../internal/Synchronization.android.kt | 5 ++ .../internal/SystemUtils.android.kt} | 0 .../workflow1/internal/Throwables.android.kt | 13 +++ .../squareup/workflow1/WorkflowInterceptor.kt | 1 + .../com/squareup/workflow1/ReflectionNames.kt | 3 - .../SimpleLoggingWorkflowInterceptorTest.kt | 36 ++++++--- .../com/squareup/workflow1/ReflectionNames.kt | 4 - .../com/squareup/workflow1/ReflectionNames.kt | 3 - .../workflow1/internal/SystemUtils.jvm.kt | 3 + .../com/squareup/workflow1/ReflectionNames.kt | 4 - workflow-ui/core-android/build.gradle.kts | 1 - 29 files changed, 200 insertions(+), 107 deletions(-) delete mode 100644 workflow-runtime-android/README.md delete mode 100644 workflow-runtime-android/api/workflow-runtime-android.api delete mode 100644 workflow-runtime-android/build.gradle.kts delete mode 100644 workflow-runtime-android/gradle.properties rename workflow-runtime/api/{ => jvm}/workflow-runtime.api (100%) rename workflow-runtime-android/dependencies/releaseRuntimeClasspath.txt => workflow-runtime/dependencies/androidRuntimeClasspath.txt (100%) rename {workflow-runtime-android/src/androidTest => workflow-runtime/src/androidDeviceTest/kotlin}/AndroidManifest.xml (100%) rename {workflow-runtime-android/src/androidTest/java => workflow-runtime/src/androidDeviceTest/kotlin}/com/squareup/workflow1/android/AndroidDispatchersRenderWorkflowInTest.kt (100%) rename {workflow-runtime-android/src/androidTest/java => workflow-runtime/src/androidDeviceTest/kotlin}/com/squareup/workflow1/android/AndroidRenderWorkflowInTest.kt (100%) create mode 100644 workflow-runtime/src/androidMain/README.md rename {workflow-runtime-android/src/main/java => workflow-runtime/src/androidMain/kotlin}/com/squareup/workflow1/android/AndroidRenderWorkflow.kt (100%) rename {workflow-runtime-android/src/main/java => workflow-runtime/src/androidMain/kotlin}/com/squareup/workflow1/android/PickledTreesnapshot.kt (100%) rename {workflow-runtime-android/src/main/java => workflow-runtime/src/androidMain/kotlin}/com/squareup/workflow1/android/TreeSnapshotSaver.kt (100%) create mode 100644 workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/internal/Synchronization.android.kt rename workflow-runtime/src/{jvmMain/kotlin/com/squareup/workflow1/internal/SystemUtils.kt => androidMain/kotlin/com/squareup/workflow1/internal/SystemUtils.android.kt} (100%) create mode 100644 workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/internal/Throwables.android.kt delete mode 100644 workflow-runtime/src/commonTest/kotlin/com/squareup/workflow1/ReflectionNames.kt delete mode 100644 workflow-runtime/src/iosTest/kotlin/com/squareup/workflow1/ReflectionNames.kt delete mode 100644 workflow-runtime/src/jsTest/kotlin/com/squareup/workflow1/ReflectionNames.kt create mode 100644 workflow-runtime/src/jvmMain/kotlin/com/squareup/workflow1/internal/SystemUtils.jvm.kt delete mode 100644 workflow-runtime/src/jvmTest/kotlin/com/squareup/workflow1/ReflectionNames.kt diff --git a/artifacts.json b/artifacts.json index 21b078b895..2cfb78b7bc 100644 --- a/artifacts.json +++ b/artifacts.json @@ -71,6 +71,15 @@ "javaVersion": 8, "publicationName": "kotlinMultiplatform" }, + { + "gradlePath": ":workflow-runtime", + "group": "com.squareup.workflow1", + "artifactId": "workflow-runtime-android", + "description": "Workflow Runtime", + "packaging": "aar", + "javaVersion": 8, + "publicationName": "android" + }, { "gradlePath": ":workflow-runtime", "group": "com.squareup.workflow1", @@ -125,15 +134,6 @@ "javaVersion": 8, "publicationName": "kotlinMultiplatform" }, - { - "gradlePath": ":workflow-runtime-android", - "group": "com.squareup.workflow1", - "artifactId": "workflow-runtime-android", - "description": "Workflow Runtime Android", - "packaging": "aar", - "javaVersion": 8, - "publicationName": "maven" - }, { "gradlePath": ":workflow-rx2", "group": "com.squareup.workflow1", diff --git a/settings.gradle.kts b/settings.gradle.kts index 00f951b069..f4982b30f7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -64,7 +64,6 @@ include( ":workflow-config:config-jvm", ":workflow-core", ":workflow-runtime", - ":workflow-runtime-android", ":workflow-rx2", ":workflow-testing", ":workflow-tracing", diff --git a/workflow-config/config-android/dependencies/releaseRuntimeClasspath.txt b/workflow-config/config-android/dependencies/releaseRuntimeClasspath.txt index 1782411506..20e8bd0fa9 100644 --- a/workflow-config/config-android/dependencies/releaseRuntimeClasspath.txt +++ b/workflow-config/config-android/dependencies/releaseRuntimeClasspath.txt @@ -1,3 +1,58 @@ +androidx.activity:activity-ktx:1.7.0 +androidx.activity:activity:1.7.0 +androidx.annotation:annotation-experimental:1.4.1 +androidx.annotation:annotation-jvm:1.8.1 +androidx.annotation:annotation:1.8.1 +androidx.arch.core:core-common:2.2.0 +androidx.arch.core:core-runtime:2.2.0 +androidx.autofill:autofill:1.0.0 +androidx.collection:collection-jvm:1.4.4 +androidx.collection:collection-ktx:1.4.4 +androidx.collection:collection:1.4.4 +androidx.compose.runtime:runtime-android:1.7.2 +androidx.compose.runtime:runtime-saveable-android:1.7.2 +androidx.compose.runtime:runtime-saveable:1.7.2 +androidx.compose.runtime:runtime:1.7.2 +androidx.compose.ui:ui-android:1.7.2 +androidx.compose.ui:ui-geometry-android:1.7.2 +androidx.compose.ui:ui-geometry:1.7.2 +androidx.compose.ui:ui-graphics-android:1.7.2 +androidx.compose.ui:ui-graphics:1.7.2 +androidx.compose.ui:ui-text-android:1.7.2 +androidx.compose.ui:ui-text:1.7.2 +androidx.compose.ui:ui-unit-android:1.7.2 +androidx.compose.ui:ui-unit:1.7.2 +androidx.compose.ui:ui-util-android:1.7.2 +androidx.compose.ui:ui-util:1.7.2 +androidx.compose:compose-bom:2024.09.02 +androidx.concurrent:concurrent-futures:1.1.0 +androidx.core:core-ktx:1.12.0 +androidx.core:core:1.12.0 +androidx.customview:customview-poolingcontainer:1.0.0 +androidx.emoji2:emoji2:1.2.0 +androidx.graphics:graphics-path:1.0.1 +androidx.interpolator:interpolator:1.0.0 +androidx.lifecycle:lifecycle-common-jvm:2.8.7 +androidx.lifecycle:lifecycle-common:2.8.7 +androidx.lifecycle:lifecycle-livedata-core:2.8.7 +androidx.lifecycle:lifecycle-process:2.8.7 +androidx.lifecycle:lifecycle-runtime-android:2.8.7 +androidx.lifecycle:lifecycle-runtime-compose-android:2.8.7 +androidx.lifecycle:lifecycle-runtime-compose:2.8.7 +androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.7 +androidx.lifecycle:lifecycle-runtime-ktx:2.8.7 +androidx.lifecycle:lifecycle-runtime:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-android:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.7 +androidx.lifecycle:lifecycle-viewmodel:2.8.7 +androidx.profileinstaller:profileinstaller:1.3.1 +androidx.savedstate:savedstate-ktx:1.2.1 +androidx.savedstate:savedstate:1.2.1 +androidx.startup:startup-runtime:1.1.1 +androidx.tracing:tracing:1.0.0 +androidx.versionedparcelable:versionedparcelable:1.1.1 +com.google.guava:listenablefuture:1.0 com.squareup.okio:okio-jvm:3.3.0 com.squareup.okio:okio:3.3.0 org.jetbrains.kotlin:kotlin-bom:2.0.21 @@ -5,6 +60,7 @@ org.jetbrains.kotlin:kotlin-stdlib-common:2.0.21 org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.21 org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21 org.jetbrains.kotlin:kotlin-stdlib:2.0.21 +org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 diff --git a/workflow-runtime-android/README.md b/workflow-runtime-android/README.md deleted file mode 100644 index f6ede30c25..0000000000 --- a/workflow-runtime-android/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Module Workflow Runtime Android - -This module is an Android library that contains utilities to start a Workflow runtime that are -specific to Android components. This contains only the 'headless' components for Workflow on -Android; i.e. no UI concerns. - -See :workflow-ui:core-android for the complimentary helpers on Android that include UI concerns: -view model persistent, `WorkflowLayout`, etc. - -It also provides a place to include tests that verify behaviour of the runtime while using -Android specific dispatchers. diff --git a/workflow-runtime-android/api/workflow-runtime-android.api b/workflow-runtime-android/api/workflow-runtime-android.api deleted file mode 100644 index 4316d5d143..0000000000 --- a/workflow-runtime-android/api/workflow-runtime-android.api +++ /dev/null @@ -1,10 +0,0 @@ -public final class com/squareup/workflow1/android/AndroidRenderWorkflowKt { - public static final fun removeWorkflowState (Landroidx/lifecycle/SavedStateHandle;)V - public static final fun renderWorkflowIn (Lcom/squareup/workflow1/Workflow;Lkotlinx/coroutines/CoroutineScope;Landroidx/lifecycle/SavedStateHandle;Ljava/util/List;Ljava/util/Set;Lcom/squareup/workflow1/WorkflowTracer;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/StateFlow; - public static final fun renderWorkflowIn (Lcom/squareup/workflow1/Workflow;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/Object;Landroidx/lifecycle/SavedStateHandle;Ljava/util/List;Ljava/util/Set;Lcom/squareup/workflow1/WorkflowTracer;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/StateFlow; - public static final fun renderWorkflowIn (Lcom/squareup/workflow1/Workflow;Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/StateFlow;Landroidx/lifecycle/SavedStateHandle;Ljava/util/List;Ljava/util/Set;Lcom/squareup/workflow1/WorkflowTracer;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/StateFlow; - public static synthetic fun renderWorkflowIn$default (Lcom/squareup/workflow1/Workflow;Lkotlinx/coroutines/CoroutineScope;Landroidx/lifecycle/SavedStateHandle;Ljava/util/List;Ljava/util/Set;Lcom/squareup/workflow1/WorkflowTracer;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/StateFlow; - public static synthetic fun renderWorkflowIn$default (Lcom/squareup/workflow1/Workflow;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/Object;Landroidx/lifecycle/SavedStateHandle;Ljava/util/List;Ljava/util/Set;Lcom/squareup/workflow1/WorkflowTracer;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/StateFlow; - public static synthetic fun renderWorkflowIn$default (Lcom/squareup/workflow1/Workflow;Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/StateFlow;Landroidx/lifecycle/SavedStateHandle;Ljava/util/List;Ljava/util/Set;Lcom/squareup/workflow1/WorkflowTracer;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/StateFlow; -} - diff --git a/workflow-runtime-android/build.gradle.kts b/workflow-runtime-android/build.gradle.kts deleted file mode 100644 index 1dfc2c7b36..0000000000 --- a/workflow-runtime-android/build.gradle.kts +++ /dev/null @@ -1,35 +0,0 @@ -plugins { - id("com.android.library") - id("kotlin-android") - id("android-defaults") - id("android-ui-tests") - id("app.cash.burst") - id("published") -} - -android { - namespace = "com.squareup.workflow1.android" - testNamespace = "$namespace.test" -} - -dependencies { - val composeBom = platform(libs.androidx.compose.bom) - - api(project(":workflow-runtime")) - api(libs.androidx.compose.ui.android) - api(libs.androidx.lifecycle.viewmodel.savedstate) - - implementation(composeBom) - implementation(project(":workflow-core")) - - androidTestImplementation(libs.androidx.activity.ktx) - androidTestImplementation(libs.androidx.lifecycle.viewmodel.ktx) - androidTestImplementation(libs.androidx.test.core) - androidTestImplementation(libs.androidx.test.truth) - androidTestImplementation(libs.kotlin.test.core) - androidTestImplementation(libs.kotlin.test.jdk) - androidTestImplementation(libs.kotlinx.coroutines.android) - androidTestImplementation(libs.kotlinx.coroutines.core) - androidTestImplementation(libs.kotlinx.coroutines.test) - androidTestImplementation(libs.squareup.papa) -} diff --git a/workflow-runtime-android/gradle.properties b/workflow-runtime-android/gradle.properties deleted file mode 100644 index 5f09c5c151..0000000000 --- a/workflow-runtime-android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -POM_ARTIFACT_ID=workflow-runtime-android -POM_NAME=Workflow Runtime Android -POM_PACKAGING=aar diff --git a/workflow-runtime/README.md b/workflow-runtime/README.md index 640411767d..fb16423c54 100644 --- a/workflow-runtime/README.md +++ b/workflow-runtime/README.md @@ -5,13 +5,13 @@ This module contains the core APIs and logic for running workflows. ## Kotlin Multiplatform This module is a Kotlin Multiplatform module. The targets currently included for build and test -are `jvm`, `ios`, and `iosSimulatorSimulatorArm64`. If you are having issues with the tests, -ensure you have the correct version of XCode installed and can launch a simulator as it's specified -in the gradle build file (Currently iPhone 14). +are `jvm`, `android`, `ios`, and `iosSimulatorSimulatorArm64`. If you are having issues with the +tests, ensure you have the correct version of XCode installed and can launch a simulator as it's +specified in the gradle build file (Currently iPhone 14). You can also choose to specify your targets for build and test with the property `workflow.targets` -as either `kmp`, `jvm`, `ios`, `js`. The default is `kmp` (all the targets). Set this in your -global `~/.gradle/gradle.properties` or specify the property in your gradle command, e.g.: +as either `kmp`, `jvm`, `android`, `ios`, `js`. The default is `kmp` (all the targets). Set this in +your global `~/.gradle/gradle.properties` or specify the property in your gradle command, e.g.: ```bash ./gradlew build -Pworkflow.targets=jvm diff --git a/workflow-runtime/api/workflow-runtime.api b/workflow-runtime/api/jvm/workflow-runtime.api similarity index 100% rename from workflow-runtime/api/workflow-runtime.api rename to workflow-runtime/api/jvm/workflow-runtime.api diff --git a/workflow-runtime/build.gradle.kts b/workflow-runtime/build.gradle.kts index 10993c7abf..89039d4df5 100644 --- a/workflow-runtime/build.gradle.kts +++ b/workflow-runtime/build.gradle.kts @@ -1,12 +1,18 @@ +import com.android.build.api.dsl.androidLibrary import com.squareup.workflow1.buildsrc.iosWithSimulatorArm64 plugins { id("kotlin-multiplatform") + id("com.android.kotlin.multiplatform.library") id("published") id("app.cash.burst") } kotlin { + // Needed for expect class Lock, which is not public API, so this doesn't add any binary compat + // risk. + compilerOptions.freeCompilerArgs.add("-Xexpect-actual-classes") + val targets = project.findProperty("workflow.targets") ?: "kmp" if (targets == "kmp" || targets == "ios") { iosWithSimulatorArm64() @@ -14,19 +20,75 @@ kotlin { if (targets == "kmp" || targets == "jvm") { jvm {} } + if (targets == "kmp" || targets == "android") { + androidLibrary { + namespace = "com.squareup.workflow1.android" + testNamespace = "$namespace.test" + + compileSdk = libs.versions.compileSdk.get().toInt() + minSdk = libs.versions.minSdk.get().toInt() + + withDeviceTestBuilder { + sourceSetTreeName = "test" + }.configure { + instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + // Disable transition and rotation animations. + animationsDisabled = true + } + } + } if (targets == "kmp" || targets == "js") { js(IR) { browser() } } - // Needed for expect class Lock, which is not public API, so this doesn't add any binary compat - // risk. - compilerOptions.freeCompilerArgs.add("-Xexpect-actual-classes") -} + sourceSets { + commonMain { + dependencies { + api(project(":workflow-core")) + api(libs.kotlinx.coroutines.core) + } + } + + commonTest { + dependencies { + implementation(libs.kotlinx.coroutines.test.common) + implementation(libs.kotlin.test.core) + } + } + + androidMain { + dependencies { + // Add Android-specific dependencies here. Note that this source set depends on + // commonMain by default and will correctly pull the Android artifacts of any KMP + // dependencies declared in commonMain. + val composeBom = project.dependencies.platform(libs.androidx.compose.bom) -dependencies { - commonMainApi(project(":workflow-core")) - commonMainApi(libs.kotlinx.coroutines.core) + api(libs.androidx.compose.ui.android) + api(libs.androidx.lifecycle.viewmodel.savedstate) - commonTestImplementation(libs.kotlinx.coroutines.test.common) - commonTestImplementation(libs.kotlin.test.core) + implementation(composeBom) + } + } + + getByName("androidDeviceTest") { + dependencies { + implementation(project(":workflow-ui:internal-testing-android")) + + implementation(libs.androidx.test.espresso.core) + implementation(libs.androidx.test.junit) + implementation(libs.squareup.leakcanary.instrumentation) + + implementation(libs.androidx.activity.ktx) + implementation(libs.androidx.lifecycle.viewmodel.ktx) + implementation(libs.androidx.test.core) + implementation(libs.androidx.test.truth) + implementation(libs.kotlin.test.core) + implementation(libs.kotlin.test.jdk) + implementation(libs.kotlinx.coroutines.android) + implementation(libs.kotlinx.coroutines.test) + implementation(libs.squareup.papa) + } + } + } } diff --git a/workflow-runtime-android/dependencies/releaseRuntimeClasspath.txt b/workflow-runtime/dependencies/androidRuntimeClasspath.txt similarity index 100% rename from workflow-runtime-android/dependencies/releaseRuntimeClasspath.txt rename to workflow-runtime/dependencies/androidRuntimeClasspath.txt diff --git a/workflow-runtime-android/src/androidTest/AndroidManifest.xml b/workflow-runtime/src/androidDeviceTest/kotlin/AndroidManifest.xml similarity index 100% rename from workflow-runtime-android/src/androidTest/AndroidManifest.xml rename to workflow-runtime/src/androidDeviceTest/kotlin/AndroidManifest.xml diff --git a/workflow-runtime-android/src/androidTest/java/com/squareup/workflow1/android/AndroidDispatchersRenderWorkflowInTest.kt b/workflow-runtime/src/androidDeviceTest/kotlin/com/squareup/workflow1/android/AndroidDispatchersRenderWorkflowInTest.kt similarity index 100% rename from workflow-runtime-android/src/androidTest/java/com/squareup/workflow1/android/AndroidDispatchersRenderWorkflowInTest.kt rename to workflow-runtime/src/androidDeviceTest/kotlin/com/squareup/workflow1/android/AndroidDispatchersRenderWorkflowInTest.kt diff --git a/workflow-runtime-android/src/androidTest/java/com/squareup/workflow1/android/AndroidRenderWorkflowInTest.kt b/workflow-runtime/src/androidDeviceTest/kotlin/com/squareup/workflow1/android/AndroidRenderWorkflowInTest.kt similarity index 100% rename from workflow-runtime-android/src/androidTest/java/com/squareup/workflow1/android/AndroidRenderWorkflowInTest.kt rename to workflow-runtime/src/androidDeviceTest/kotlin/com/squareup/workflow1/android/AndroidRenderWorkflowInTest.kt diff --git a/workflow-runtime/src/androidMain/README.md b/workflow-runtime/src/androidMain/README.md new file mode 100644 index 0000000000..af4bc6316b --- /dev/null +++ b/workflow-runtime/src/androidMain/README.md @@ -0,0 +1,10 @@ +# Android vs JVM targets + +The default KMP +["hierarchy template"](https://www.jetbrains.com/help/kotlin-multiplatform-dev/multiplatform-hierarchy.html#see-the-full-hierarchy-template) +configures `androidMain` and `jvmMain` to be entirely separate targets, even though Android *can* +be made to be a child of JVM. Changing this requires completely wiring up all targets ourselves +though, so for now we've left them separate to simplify gradle config. If there ends up being too +much code duplication, we can either make `androidMain` a child of `jvmMain`, or introduce a new +shared target that includes both of them. Compose, for example, uses a structure where `jvm` is the +shared parent of both `android` and `desktop`. diff --git a/workflow-runtime-android/src/main/java/com/squareup/workflow1/android/AndroidRenderWorkflow.kt b/workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/android/AndroidRenderWorkflow.kt similarity index 100% rename from workflow-runtime-android/src/main/java/com/squareup/workflow1/android/AndroidRenderWorkflow.kt rename to workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/android/AndroidRenderWorkflow.kt diff --git a/workflow-runtime-android/src/main/java/com/squareup/workflow1/android/PickledTreesnapshot.kt b/workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/android/PickledTreesnapshot.kt similarity index 100% rename from workflow-runtime-android/src/main/java/com/squareup/workflow1/android/PickledTreesnapshot.kt rename to workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/android/PickledTreesnapshot.kt diff --git a/workflow-runtime-android/src/main/java/com/squareup/workflow1/android/TreeSnapshotSaver.kt b/workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/android/TreeSnapshotSaver.kt similarity index 100% rename from workflow-runtime-android/src/main/java/com/squareup/workflow1/android/TreeSnapshotSaver.kt rename to workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/android/TreeSnapshotSaver.kt diff --git a/workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/internal/Synchronization.android.kt b/workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/internal/Synchronization.android.kt new file mode 100644 index 0000000000..e84a031233 --- /dev/null +++ b/workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/internal/Synchronization.android.kt @@ -0,0 +1,5 @@ +package com.squareup.workflow1.internal + +internal actual typealias Lock = Any + +internal actual inline fun Lock.withLock(block: () -> R): R = synchronized(this, block) diff --git a/workflow-runtime/src/jvmMain/kotlin/com/squareup/workflow1/internal/SystemUtils.kt b/workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/internal/SystemUtils.android.kt similarity index 100% rename from workflow-runtime/src/jvmMain/kotlin/com/squareup/workflow1/internal/SystemUtils.kt rename to workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/internal/SystemUtils.android.kt diff --git a/workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/internal/Throwables.android.kt b/workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/internal/Throwables.android.kt new file mode 100644 index 0000000000..f4ddf5e2cf --- /dev/null +++ b/workflow-runtime/src/androidMain/kotlin/com/squareup/workflow1/internal/Throwables.android.kt @@ -0,0 +1,13 @@ +package com.squareup.workflow1.internal + +actual fun T.withKey(stackTraceKey: Any): T = apply { + val realTop = stackTrace[0] + val fakeTop = StackTraceElement( + // Real class name to ensure that we are still "in project". + realTop.className, + "fakeMethodForCrashGrouping", + /* fileName = */ stackTraceKey.toString(), + /* lineNumber = */ stackTraceKey.hashCode() + ) + stackTrace = stackTrace.toMutableList().apply { add(0, fakeTop) }.toTypedArray() +} diff --git a/workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/WorkflowInterceptor.kt b/workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/WorkflowInterceptor.kt index 435e2c8667..db4a6cdaf5 100644 --- a/workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/WorkflowInterceptor.kt +++ b/workflow-runtime/src/commonMain/kotlin/com/squareup/workflow1/WorkflowInterceptor.kt @@ -1,6 +1,7 @@ package com.squareup.workflow1 import com.squareup.workflow1.WorkflowInterceptor.RenderContextInterceptor +import com.squareup.workflow1.WorkflowInterceptor.RuntimeUpdate import com.squareup.workflow1.WorkflowInterceptor.WorkflowSession import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job diff --git a/workflow-runtime/src/commonTest/kotlin/com/squareup/workflow1/ReflectionNames.kt b/workflow-runtime/src/commonTest/kotlin/com/squareup/workflow1/ReflectionNames.kt deleted file mode 100644 index 7761301410..0000000000 --- a/workflow-runtime/src/commonTest/kotlin/com/squareup/workflow1/ReflectionNames.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.squareup.workflow1 - -expect val ILLEGAL_ARGUMENT_EXCEPTION_NAME: String diff --git a/workflow-runtime/src/commonTest/kotlin/com/squareup/workflow1/SimpleLoggingWorkflowInterceptorTest.kt b/workflow-runtime/src/commonTest/kotlin/com/squareup/workflow1/SimpleLoggingWorkflowInterceptorTest.kt index 477326786a..bdc4494b25 100644 --- a/workflow-runtime/src/commonTest/kotlin/com/squareup/workflow1/SimpleLoggingWorkflowInterceptorTest.kt +++ b/workflow-runtime/src/commonTest/kotlin/com/squareup/workflow1/SimpleLoggingWorkflowInterceptorTest.kt @@ -8,6 +8,7 @@ import kotlin.reflect.KType import kotlin.reflect.typeOf import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue import kotlin.test.fail internal class SimpleLoggingWorkflowInterceptorTest { @@ -18,7 +19,7 @@ internal class SimpleLoggingWorkflowInterceptorTest { interceptor.onSessionStarted(scope, TestWorkflowSession) scope.cancel() - assertEquals(ErrorLoggingInterceptor.EXPECTED_ERRORS, interceptor.errors) + assertAllMatch(ErrorLoggingInterceptor.EXPECTED_ERRORS, interceptor.errors) } @Test fun onInitialState_handles_logging_exceptions() { @@ -31,14 +32,14 @@ internal class SimpleLoggingWorkflowInterceptorTest { TestWorkflowSession ) - assertEquals(ErrorLoggingInterceptor.EXPECTED_ERRORS, interceptor.errors) + assertAllMatch(ErrorLoggingInterceptor.EXPECTED_ERRORS, interceptor.errors) } @Test fun onPropsChanged_handles_logging_exceptions() { val interceptor = ErrorLoggingInterceptor() interceptor.onPropsChanged(Unit, Unit, Unit, { _, _, _ -> }, TestWorkflowSession) - assertEquals(ErrorLoggingInterceptor.EXPECTED_ERRORS, interceptor.errors) + assertAllMatch(ErrorLoggingInterceptor.EXPECTED_ERRORS, interceptor.errors) } @Test fun onRender_handles_logging_exceptions() { @@ -52,14 +53,14 @@ internal class SimpleLoggingWorkflowInterceptorTest { TestWorkflowSession, ) - assertEquals(ErrorLoggingInterceptor.EXPECTED_ERRORS, interceptor.errors) + assertAllMatch(ErrorLoggingInterceptor.EXPECTED_ERRORS, interceptor.errors) } @Test fun onSnapshotState_handles_logging_exceptions() { val interceptor = ErrorLoggingInterceptor() interceptor.onSnapshotState(Unit, { null }, TestWorkflowSession) - assertEquals(ErrorLoggingInterceptor.EXPECTED_ERRORS, interceptor.errors) + assertAllMatch(ErrorLoggingInterceptor.EXPECTED_ERRORS, interceptor.errors) } private open class ErrorLoggingInterceptor : SimpleLoggingWorkflowInterceptor() { @@ -75,10 +76,27 @@ internal class SimpleLoggingWorkflowInterceptorTest { companion object { val EXPECTED_ERRORS = listOf( - "ErrorLoggingInterceptor.logBeforeMethod threw exception:\n" + - ILLEGAL_ARGUMENT_EXCEPTION_NAME, - "ErrorLoggingInterceptor.logAfterMethod threw exception:\n" + - ILLEGAL_ARGUMENT_EXCEPTION_NAME + ( + "ErrorLoggingInterceptor\\.logBeforeMethod threw exception:\n" + + ".+IllegalArgumentException.*" + ).toRegex(), + ( + "ErrorLoggingInterceptor\\.logAfterMethod threw exception:\n" + + ".+IllegalArgumentException.*" + ).toRegex() + ) + } + } + + private fun assertAllMatch( + expected: List, + actual: List + ) { + assertEquals(expected.size, actual.size) + expected.zip(actual).forEachIndexed { index, (expectedPattern, actualString) -> + assertTrue( + expectedPattern.matches(actualString), + "Expected string at index $index to match pattern /$expectedPattern/: \"$actualString\"" ) } } diff --git a/workflow-runtime/src/iosTest/kotlin/com/squareup/workflow1/ReflectionNames.kt b/workflow-runtime/src/iosTest/kotlin/com/squareup/workflow1/ReflectionNames.kt deleted file mode 100644 index 29388c00c1..0000000000 --- a/workflow-runtime/src/iosTest/kotlin/com/squareup/workflow1/ReflectionNames.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.squareup.workflow1 - -actual val ILLEGAL_ARGUMENT_EXCEPTION_NAME = - IllegalArgumentException::class.qualifiedName.toString() diff --git a/workflow-runtime/src/jsTest/kotlin/com/squareup/workflow1/ReflectionNames.kt b/workflow-runtime/src/jsTest/kotlin/com/squareup/workflow1/ReflectionNames.kt deleted file mode 100644 index 405787e502..0000000000 --- a/workflow-runtime/src/jsTest/kotlin/com/squareup/workflow1/ReflectionNames.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.squareup.workflow1 - -actual val ILLEGAL_ARGUMENT_EXCEPTION_NAME = IllegalArgumentException::class.simpleName.toString() diff --git a/workflow-runtime/src/jvmMain/kotlin/com/squareup/workflow1/internal/SystemUtils.jvm.kt b/workflow-runtime/src/jvmMain/kotlin/com/squareup/workflow1/internal/SystemUtils.jvm.kt new file mode 100644 index 0000000000..354e7ef9ca --- /dev/null +++ b/workflow-runtime/src/jvmMain/kotlin/com/squareup/workflow1/internal/SystemUtils.jvm.kt @@ -0,0 +1,3 @@ +package com.squareup.workflow1.internal + +internal actual fun currentTimeMillis(): Long = System.currentTimeMillis() diff --git a/workflow-runtime/src/jvmTest/kotlin/com/squareup/workflow1/ReflectionNames.kt b/workflow-runtime/src/jvmTest/kotlin/com/squareup/workflow1/ReflectionNames.kt deleted file mode 100644 index 29388c00c1..0000000000 --- a/workflow-runtime/src/jvmTest/kotlin/com/squareup/workflow1/ReflectionNames.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.squareup.workflow1 - -actual val ILLEGAL_ARGUMENT_EXCEPTION_NAME = - IllegalArgumentException::class.qualifiedName.toString() diff --git a/workflow-ui/core-android/build.gradle.kts b/workflow-ui/core-android/build.gradle.kts index 1d489d7e81..89adc8b266 100644 --- a/workflow-ui/core-android/build.gradle.kts +++ b/workflow-ui/core-android/build.gradle.kts @@ -23,7 +23,6 @@ dependencies { // Needs to be API for the WorkflowInterceptor argument to WorkflowRunner.Config. api(project(":workflow-runtime")) - api(project(":workflow-runtime-android")) api(project(":workflow-ui:core-common")) compileOnly(libs.androidx.viewbinding)