diff --git a/benchmarks/dungeon-benchmark/build.gradle.kts b/benchmarks/dungeon-benchmark/build.gradle.kts index 6f251f217..0d9c6e238 100644 --- a/benchmarks/dungeon-benchmark/build.gradle.kts +++ b/benchmarks/dungeon-benchmark/build.gradle.kts @@ -1,3 +1,6 @@ +import com.squareup.workflow1.libsCatalog +import com.squareup.workflow1.version + plugins { id("com.android.test") id("org.jetbrains.kotlin.android") @@ -7,7 +10,7 @@ plugins { // dependencies that those include. android { - compileSdk = 32 + compileSdk = libsCatalog.version("compileSdk").toInt() compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -21,7 +24,7 @@ android { defaultConfig { minSdk = 23 - targetSdk = 32 + targetSdk = libsCatalog.version("targetSdk").toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/benchmarks/performance-poetry/complex-benchmark/build.gradle.kts b/benchmarks/performance-poetry/complex-benchmark/build.gradle.kts index ee759e3fd..239d5dd7a 100644 --- a/benchmarks/performance-poetry/complex-benchmark/build.gradle.kts +++ b/benchmarks/performance-poetry/complex-benchmark/build.gradle.kts @@ -1,3 +1,6 @@ +import com.squareup.workflow1.libsCatalog +import com.squareup.workflow1.version + plugins { id("com.android.test") id("org.jetbrains.kotlin.android") @@ -7,7 +10,7 @@ plugins { // dependencies that those include. android { - compileSdk = 32 + compileSdk = libsCatalog.version("compileSdk").toInt() compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -21,7 +24,7 @@ android { defaultConfig { minSdk = 26 - targetSdk = 32 + targetSdk = libsCatalog.version("targetSdk").toInt() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/benchmarks/performance-poetry/complex-poetry/build.gradle.kts b/benchmarks/performance-poetry/complex-poetry/build.gradle.kts index 62f9d8e00..be0072991 100644 --- a/benchmarks/performance-poetry/complex-poetry/build.gradle.kts +++ b/benchmarks/performance-poetry/complex-poetry/build.gradle.kts @@ -1,10 +1,13 @@ +import com.squareup.workflow1.libsCatalog +import com.squareup.workflow1.version + plugins { id("com.android.application") `kotlin-android` id("kotlin-parcelize") } android { - compileSdk = 32 + compileSdk = libsCatalog.version("compileSdk").toInt() compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -16,7 +19,7 @@ android { } defaultConfig { - targetSdk = 32 + targetSdk = libsCatalog.version("targetSdk").toInt() minSdk = 29 applicationId = "com.squareup.benchmarks.performance.complex.poetry" diff --git a/buildSrc/src/main/java/Versions.kt b/buildSrc/src/main/java/Versions.kt index e23baf8bb..6f2234c4d 100644 --- a/buildSrc/src/main/java/Versions.kt +++ b/buildSrc/src/main/java/Versions.kt @@ -1,5 +1,5 @@ @Suppress("UNUSED") // used in Groovy convention scripts object Versions { - const val compileSdk = 31 - const val targetSdk = 30 + const val compileSdk = 33 + const val targetSdk = 33 } diff --git a/buildSrc/src/main/java/android-defaults.gradle.kts b/buildSrc/src/main/java/android-defaults.gradle.kts index 33cc37a08..aec33b29f 100644 --- a/buildSrc/src/main/java/android-defaults.gradle.kts +++ b/buildSrc/src/main/java/android-defaults.gradle.kts @@ -1,7 +1,9 @@ import com.android.build.gradle.TestedExtension +import com.squareup.workflow1.libsCatalog +import com.squareup.workflow1.version configure { - compileSdkVersion(31) + compileSdkVersion(libsCatalog.version("compileSdk").toInt()) compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -9,8 +11,8 @@ configure { } defaultConfig { - minSdk = 21 - targetSdk = 30 + minSdk = libsCatalog.version("minSdk").toInt() + targetSdk = libsCatalog.version("targetSdk").toInt() versionCode = 1 versionName = "1.0" } diff --git a/buildSrc/src/main/java/android-sample-app.gradle.kts b/buildSrc/src/main/java/android-sample-app.gradle.kts index ac3a62524..34418c28c 100644 --- a/buildSrc/src/main/java/android-sample-app.gradle.kts +++ b/buildSrc/src/main/java/android-sample-app.gradle.kts @@ -1,4 +1,5 @@ import com.android.build.gradle.TestedExtension +import com.android.build.gradle.internal.dsl.BaseAppModuleExtension import com.squareup.workflow1.library import com.squareup.workflow1.libsCatalog @@ -11,6 +12,12 @@ configure { buildFeatures.viewBinding = true } +configure { + lint { + baseline = file("lint-baseline.xml") + } +} + dependencies { "implementation"(project(":workflow-core")) "implementation"(project(":workflow-runtime")) diff --git a/buildSrc/src/main/java/android-ui-tests.gradle.kts b/buildSrc/src/main/java/android-ui-tests.gradle.kts index 5e9a5e37c..542a33f26 100644 --- a/buildSrc/src/main/java/android-ui-tests.gradle.kts +++ b/buildSrc/src/main/java/android-ui-tests.gradle.kts @@ -9,7 +9,6 @@ plugins { configure { defaultConfig { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - testInstrumentationRunnerArguments["listener"] = "leakcanary.FailTestOnLeakRunListener" } testOptions { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 105d52596..4019c2d33 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,21 +2,22 @@ androidTools = "7.4.1" -compileSdk = "31" -minSdkVersion = "21" -targetSdk = "30" +compileSdk = "33" +minSdk = "21" +targetSdk = "33" -androidx-activity = "1.3.0" -androidx-appcompat = "1.3.1" +androidx-activity = "1.6.1" +androidx-appcompat = "1.6.1" androidx-benchmark = "1.1.1" androidx-cardview = "1.0.0" -androidx-compose = "1.1.0-rc01" androidx-compose-compiler = "1.3.2" +# see https://developer.android.com/jetpack/compose/bom/bom-mapping +androidx-compose-bom = "2023.01.00" androidx-constraintlayout = "2.1.4" androidx-core = "1.6.0" androidx-fragment = "1.3.6" androidx-gridlayout = "1.0.0" -androidx-lifecycle = "2.4.0" +androidx-lifecycle = "2.5.1" androidx-navigation = "2.4.0-alpha09" androidx-paging = "3.0.1" androidx-profileinstaller = "1.2.0-alpha02" @@ -24,9 +25,10 @@ androidx-recyclerview = "1.2.1" androidx-room = "2.4.0-alpha04" androidx-savedstate = "1.1.0" androidx-startup = "1.1.0" -androidx-test = "1.3.0" +androidx-test = "1.5.0" androidx-test-espresso = "3.3.0" androidx-test-junit-ext = "1.1.3" +androidx-test-runner = "1.5.2" androidx-test-truth-ext = "1.4.0" androidx-tracing = "1.1.0" androidx-transition = "1.4.1" @@ -64,14 +66,14 @@ mockito-core = "3.3.3" mockito-kotlin = "3.2.0" mockk = "1.11.0" -robolectric = "4.6.1" +robolectric = "4.9.2" rxjava2-android = "2.1.1" rxjava2-core = "2.2.21" squareup-curtains = "1.2.2" squareup-cycler = "0.1.9" -squareup-leakcanary = "2.8.1" +squareup-leakcanary = "2.10" squareup-moshi = "1.13.0" squareup-okhttp = "4.9.1" squareup-okio = "3.0.0" @@ -115,20 +117,22 @@ androidx-cardview = { module = "androidx.cardview:cardview", version.ref = "andr androidx-compose-compiler = { module = "androidx.compose.compiler:compiler", version.ref = "androidx-compose-compiler" } -androidx-compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "androidx-compose" } -androidx-compose-foundation-layout = { module = "androidx.compose.foundation:foundation-layout", version.ref = "androidx-compose" } +androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "androidx-compose-bom" } -androidx-compose-material = { module = "androidx.compose.material:material", version.ref = "androidx-compose" } +androidx-compose-foundation = { module = "androidx.compose.foundation:foundation"} +androidx-compose-foundation-layout = { module = "androidx.compose.foundation:foundation-layout"} -androidx-compose-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "androidx-compose" } -androidx-compose-runtime-saveable = { module = "androidx.compose.runtime:runtime-saveable", version.ref = "androidx-compose" } +androidx-compose-material = { module = "androidx.compose.material:material"} -androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "androidx-compose" } -androidx-compose-ui-geometry = { module = "androidx.compose.ui:ui-geometry", version.ref = "androidx-compose" } -androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics", version.ref = "androidx-compose" } -androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx-compose" } -androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "androidx-compose" } -androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "androidx-compose" } +androidx-compose-runtime = { module = "androidx.compose.runtime:runtime"} +androidx-compose-runtime-saveable = { module = "androidx.compose.runtime:runtime-saveable"} + +androidx-compose-ui = { module = "androidx.compose.ui:ui"} +androidx-compose-ui-geometry = { module = "androidx.compose.ui:ui-geometry"} +androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics"} +androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4"} +androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling"} +androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview"} androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" } @@ -160,7 +164,7 @@ androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", androidx-test-espresso-idlingResource = { module = "androidx.test.espresso:espresso-idling-resource", version.ref = "androidx-test-espresso" } androidx-test-espresso-intents = { module = "androidx.test.espresso:espresso-intents", version.ref = "androidx-test-espresso" } androidx-test-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-junit-ext" } -androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test" } +androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test-runner" } androidx-test-truth = { module = "androidx.test.ext:truth", version.ref = "androidx-test-truth-ext" } androidx-test-uiautomator = "androidx.test.uiautomator:uiautomator:2.2.0" diff --git a/samples/compose-samples/build.gradle.kts b/samples/compose-samples/build.gradle.kts index f7b69d236..884670c89 100644 --- a/samples/compose-samples/build.gradle.kts +++ b/samples/compose-samples/build.gradle.kts @@ -20,7 +20,10 @@ android { } dependencies { + val composeBom = platform(libs.androidx.compose.bom) + androidTestImplementation(libs.androidx.activity.core) + androidTestImplementation(composeBom) androidTestImplementation(libs.androidx.compose.ui) androidTestImplementation(libs.androidx.compose.ui.test.junit4) androidTestImplementation(libs.androidx.test.core) @@ -33,6 +36,7 @@ dependencies { implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.core) + implementation(composeBom) implementation(libs.androidx.compose.foundation) implementation(libs.androidx.compose.foundation.layout) implementation(libs.androidx.compose.material) diff --git a/samples/compose-samples/lint-baseline.xml b/samples/compose-samples/lint-baseline.xml new file mode 100644 index 000000000..48c45f24a --- /dev/null +++ b/samples/compose-samples/lint-baseline.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocompose/HelloComposeTest.kt b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocompose/HelloComposeTest.kt index 1cccd8895..f90aea904 100644 --- a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocompose/HelloComposeTest.kt +++ b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocompose/HelloComposeTest.kt @@ -6,12 +6,12 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.compose.settleForNextRendering import com.squareup.workflow1.ui.internal.test.retry import kotlinx.coroutines.runBlocking +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocomposebinding/HelloBindingTest.kt b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocomposebinding/HelloBindingTest.kt index d6f4ba3f3..a79a295d9 100644 --- a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocomposebinding/HelloBindingTest.kt +++ b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocomposebinding/HelloBindingTest.kt @@ -6,9 +6,9 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocomposeworkflow/HelloComposeWorkflowTest.kt b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocomposeworkflow/HelloComposeWorkflowTest.kt index bb2eb58fe..fb9e131c1 100644 --- a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocomposeworkflow/HelloComposeWorkflowTest.kt +++ b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/hellocomposeworkflow/HelloComposeWorkflowTest.kt @@ -6,9 +6,9 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/inlinerendering/InlineRenderingTest.kt b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/inlinerendering/InlineRenderingTest.kt index a38736b47..b6e3566ed 100644 --- a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/inlinerendering/InlineRenderingTest.kt +++ b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/inlinerendering/InlineRenderingTest.kt @@ -7,12 +7,12 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.compose.settleForNextRendering import com.squareup.workflow1.ui.internal.test.retry import kotlinx.coroutines.runBlocking +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/launcher/SampleLauncherTest.kt b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/launcher/SampleLauncherTest.kt index 4a8fd5032..cd0a22f3c 100644 --- a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/launcher/SampleLauncherTest.kt +++ b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/launcher/SampleLauncherTest.kt @@ -1,6 +1,5 @@ package com.squareup.sample.compose.launcher -import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasScrollToIndexAction import androidx.compose.ui.test.junit4.createAndroidComposeRule @@ -12,9 +11,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.squareup.sample.compose.R import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain @@ -31,7 +30,6 @@ class SampleLauncherTest { .around(composeRule) .around(IdlingDispatcherRule) - @OptIn(ExperimentalTestApi::class) @Test fun allSamplesLaunch() { val appName = diff --git a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/nestedrenderings/NestedRenderingsTest.kt b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/nestedrenderings/NestedRenderingsTest.kt index 504549c45..3a1376f94 100644 --- a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/nestedrenderings/NestedRenderingsTest.kt +++ b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/nestedrenderings/NestedRenderingsTest.kt @@ -10,9 +10,9 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/preview/PreviewTest.kt b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/preview/PreviewTest.kt index 5e538a6fe..ec222c5fd 100644 --- a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/preview/PreviewTest.kt +++ b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/preview/PreviewTest.kt @@ -6,9 +6,9 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/textinput/TextInputTest.kt b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/textinput/TextInputTest.kt index a77e60c0b..adfc481f8 100644 --- a/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/textinput/TextInputTest.kt +++ b/samples/compose-samples/src/androidTest/java/com/squareup/sample/compose/textinput/TextInputTest.kt @@ -10,12 +10,12 @@ import androidx.compose.ui.test.performTextInput import androidx.compose.ui.test.performTextReplacement import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.compose.settleForNextRendering import com.squareup.workflow1.ui.internal.test.retry import kotlinx.coroutines.runBlocking +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/compose-samples/src/main/AndroidManifest.xml b/samples/compose-samples/src/main/AndroidManifest.xml index 2d3dc35ba..4df8623c7 100644 --- a/samples/compose-samples/src/main/AndroidManifest.xml +++ b/samples/compose-samples/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon" > - + diff --git a/samples/compose-samples/src/main/java/com/squareup/sample/compose/inlinerendering/InlineRenderingWorkflow.kt b/samples/compose-samples/src/main/java/com/squareup/sample/compose/inlinerendering/InlineRenderingWorkflow.kt index f02aa0e62..8e53b7026 100644 --- a/samples/compose-samples/src/main/java/com/squareup/sample/compose/inlinerendering/InlineRenderingWorkflow.kt +++ b/samples/compose-samples/src/main/java/com/squareup/sample/compose/inlinerendering/InlineRenderingWorkflow.kt @@ -53,9 +53,8 @@ object InlineRenderingWorkflow : StatefulWorkflow + LazyColumn( + contentPadding = padding + ) { + items(samples) { sample -> + SampleItem(sample) } } } @@ -152,7 +153,7 @@ private fun Modifier.disableTouchInput(): Modifier = pointerInput(Unit) { awaitPointerEventScope { awaitPointerEvent(Initial).let { event -> event.changes.forEach { change -> - change.consumeDownChange() + if (change.pressed != change.previousPressed) change.consume() } } } diff --git a/samples/containers/android/build.gradle.kts b/samples/containers/android/build.gradle.kts index 105d121ea..51ccdd403 100644 --- a/samples/containers/android/build.gradle.kts +++ b/samples/containers/android/build.gradle.kts @@ -12,7 +12,10 @@ android { } dependencies { + val composeBom = platform(libs.androidx.compose.bom) + androidTestImplementation(libs.androidx.activity.core) + androidTestImplementation(composeBom) androidTestImplementation(libs.androidx.compose.ui) androidTestImplementation(libs.androidx.compose.ui.test.junit4) androidTestImplementation(libs.androidx.test.core) diff --git a/samples/containers/app-poetry/lint-baseline.xml b/samples/containers/app-poetry/lint-baseline.xml new file mode 100644 index 000000000..ed333fbf8 --- /dev/null +++ b/samples/containers/app-poetry/lint-baseline.xml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/samples/containers/app-poetry/src/androidTest/java/com/squareup/sample/poetryapp/PoetryAppTest.kt b/samples/containers/app-poetry/src/androidTest/java/com/squareup/sample/poetryapp/PoetryAppTest.kt index 775ccc044..6bc56540d 100644 --- a/samples/containers/app-poetry/src/androidTest/java/com/squareup/sample/poetryapp/PoetryAppTest.kt +++ b/samples/containers/app-poetry/src/androidTest/java/com/squareup/sample/poetryapp/PoetryAppTest.kt @@ -7,9 +7,9 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.sample.container.poetryapp.R import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.inAnyView +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/containers/app-poetry/src/main/AndroidManifest.xml b/samples/containers/app-poetry/src/main/AndroidManifest.xml index 8b398d0b2..26594ffd1 100644 --- a/samples/containers/app-poetry/src/main/AndroidManifest.xml +++ b/samples/containers/app-poetry/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon" > - + diff --git a/samples/containers/app-raven/lint-baseline.xml b/samples/containers/app-raven/lint-baseline.xml new file mode 100644 index 000000000..ed333fbf8 --- /dev/null +++ b/samples/containers/app-raven/lint-baseline.xml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/samples/containers/app-raven/src/androidTest/java/com/squareup/sample/ravenapp/RavenAppTest.kt b/samples/containers/app-raven/src/androidTest/java/com/squareup/sample/ravenapp/RavenAppTest.kt index 753c6186e..b1644b1e9 100644 --- a/samples/containers/app-raven/src/androidTest/java/com/squareup/sample/ravenapp/RavenAppTest.kt +++ b/samples/containers/app-raven/src/androidTest/java/com/squareup/sample/ravenapp/RavenAppTest.kt @@ -6,9 +6,9 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.inAnyView +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/containers/app-raven/src/main/AndroidManifest.xml b/samples/containers/app-raven/src/main/AndroidManifest.xml index e5f581d7c..17c7cdd28 100644 --- a/samples/containers/app-raven/src/main/AndroidManifest.xml +++ b/samples/containers/app-raven/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon" > - + diff --git a/samples/containers/hello-back-button/lint-baseline.xml b/samples/containers/hello-back-button/lint-baseline.xml new file mode 100644 index 000000000..a1c902b19 --- /dev/null +++ b/samples/containers/hello-back-button/lint-baseline.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/samples/containers/hello-back-button/src/androidTest/java/com/squareup/sample/hellobackbutton/HelloBackButtonEspressoTest.kt b/samples/containers/hello-back-button/src/androidTest/java/com/squareup/sample/hellobackbutton/HelloBackButtonEspressoTest.kt index 3971552b9..c353358a5 100644 --- a/samples/containers/hello-back-button/src/androidTest/java/com/squareup/sample/hellobackbutton/HelloBackButtonEspressoTest.kt +++ b/samples/containers/hello-back-button/src/androidTest/java/com/squareup/sample/hellobackbutton/HelloBackButtonEspressoTest.kt @@ -8,11 +8,11 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.actuallyPressBack import com.squareup.workflow1.ui.internal.test.inAnyView import com.squareup.workflow1.ui.internal.test.retryBlocking +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/containers/hello-back-button/src/main/AndroidManifest.xml b/samples/containers/hello-back-button/src/main/AndroidManifest.xml index 4789f9e25..a682370fd 100644 --- a/samples/containers/hello-back-button/src/main/AndroidManifest.xml +++ b/samples/containers/hello-back-button/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon" > - + diff --git a/samples/dungeon/app/lint-baseline.xml b/samples/dungeon/app/lint-baseline.xml new file mode 100644 index 000000000..37e236431 --- /dev/null +++ b/samples/dungeon/app/lint-baseline.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/dungeon/app/src/androidTest/java/com/squareup/sample/dungeon/DungeonAppTest.kt b/samples/dungeon/app/src/androidTest/java/com/squareup/sample/dungeon/DungeonAppTest.kt index 2bf2c51b5..c38a2e8e0 100644 --- a/samples/dungeon/app/src/androidTest/java/com/squareup/sample/dungeon/DungeonAppTest.kt +++ b/samples/dungeon/app/src/androidTest/java/com/squareup/sample/dungeon/DungeonAppTest.kt @@ -6,9 +6,9 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.inAnyView +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/dungeon/app/src/main/AndroidManifest.xml b/samples/dungeon/app/src/main/AndroidManifest.xml index a8cc3bb22..99776c041 100644 --- a/samples/dungeon/app/src/main/AndroidManifest.xml +++ b/samples/dungeon/app/src/main/AndroidManifest.xml @@ -12,7 +12,8 @@ + android:screenOrientation="portrait" + android:exported="true"> diff --git a/samples/dungeon/timemachine-shakeable/src/main/java/com/squareup/sample/timemachine/shakeable/ShakeWorker.kt b/samples/dungeon/timemachine-shakeable/src/main/java/com/squareup/sample/timemachine/shakeable/ShakeWorker.kt index 79b69b047..7d71869cc 100644 --- a/samples/dungeon/timemachine-shakeable/src/main/java/com/squareup/sample/timemachine/shakeable/ShakeWorker.kt +++ b/samples/dungeon/timemachine-shakeable/src/main/java/com/squareup/sample/timemachine/shakeable/ShakeWorker.kt @@ -30,7 +30,7 @@ class ShakeWorker(private val context: Context) : Worker { private val realShakes = callbackFlow { val shakeDetector = ShakeDetector { trySend(Unit).isSuccess } - shakeDetector.start(sensorManager) + shakeDetector.start(sensorManager, SensorManager.SENSOR_DELAY_GAME) awaitClose { shakeDetector.stop() } } diff --git a/samples/hello-workflow-fragment/lint-baseline.xml b/samples/hello-workflow-fragment/lint-baseline.xml new file mode 100644 index 000000000..5d55093fb --- /dev/null +++ b/samples/hello-workflow-fragment/lint-baseline.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/hello-workflow-fragment/src/androidTest/java/com/squareup/sample/helloworkflowfragment/HelloWorkflowFragmentAppTest.kt b/samples/hello-workflow-fragment/src/androidTest/java/com/squareup/sample/helloworkflowfragment/HelloWorkflowFragmentAppTest.kt index 73640814a..78ad2d60a 100644 --- a/samples/hello-workflow-fragment/src/androidTest/java/com/squareup/sample/helloworkflowfragment/HelloWorkflowFragmentAppTest.kt +++ b/samples/hello-workflow-fragment/src/androidTest/java/com/squareup/sample/helloworkflowfragment/HelloWorkflowFragmentAppTest.kt @@ -7,9 +7,9 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.inAnyView +import leakcanary.DetectLeaksAfterTestSuccess import org.hamcrest.Matchers.containsString import org.junit.Rule import org.junit.Test diff --git a/samples/hello-workflow-fragment/src/main/AndroidManifest.xml b/samples/hello-workflow-fragment/src/main/AndroidManifest.xml index 38b8dc545..7571e4d6b 100644 --- a/samples/hello-workflow-fragment/src/main/AndroidManifest.xml +++ b/samples/hello-workflow-fragment/src/main/AndroidManifest.xml @@ -8,7 +8,8 @@ android:allowBackup="true" > - + diff --git a/samples/hello-workflow/lint-baseline.xml b/samples/hello-workflow/lint-baseline.xml new file mode 100644 index 000000000..ed333fbf8 --- /dev/null +++ b/samples/hello-workflow/lint-baseline.xml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/samples/hello-workflow/src/androidTest/java/com/squareup/sample/helloworkflow/HelloWorkflowAppTest.kt b/samples/hello-workflow/src/androidTest/java/com/squareup/sample/helloworkflow/HelloWorkflowAppTest.kt index b9045d4b8..66dbdb855 100644 --- a/samples/hello-workflow/src/androidTest/java/com/squareup/sample/helloworkflow/HelloWorkflowAppTest.kt +++ b/samples/hello-workflow/src/androidTest/java/com/squareup/sample/helloworkflow/HelloWorkflowAppTest.kt @@ -7,9 +7,9 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.inAnyView +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/samples/hello-workflow/src/main/AndroidManifest.xml b/samples/hello-workflow/src/main/AndroidManifest.xml index e3614bb15..131b4bc73 100644 --- a/samples/hello-workflow/src/main/AndroidManifest.xml +++ b/samples/hello-workflow/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon" > - + diff --git a/samples/stub-visibility/lint-baseline.xml b/samples/stub-visibility/lint-baseline.xml new file mode 100644 index 000000000..2ee1c7d31 --- /dev/null +++ b/samples/stub-visibility/lint-baseline.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/samples/stub-visibility/src/androidTest/java/com/squareup/sample/stubvisibility/StubVisibilityAppTest.kt b/samples/stub-visibility/src/androidTest/java/com/squareup/sample/stubvisibility/StubVisibilityAppTest.kt index eac0b7314..a5f216963 100644 --- a/samples/stub-visibility/src/androidTest/java/com/squareup/sample/stubvisibility/StubVisibilityAppTest.kt +++ b/samples/stub-visibility/src/androidTest/java/com/squareup/sample/stubvisibility/StubVisibilityAppTest.kt @@ -8,9 +8,9 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.inAnyView +import leakcanary.DetectLeaksAfterTestSuccess import org.hamcrest.CoreMatchers.not import org.junit.Rule import org.junit.Test diff --git a/samples/stub-visibility/src/main/AndroidManifest.xml b/samples/stub-visibility/src/main/AndroidManifest.xml index ab0c63d5e..0fc613587 100644 --- a/samples/stub-visibility/src/main/AndroidManifest.xml +++ b/samples/stub-visibility/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon" > - + diff --git a/samples/tictactoe/app/lint-baseline.xml b/samples/tictactoe/app/lint-baseline.xml new file mode 100644 index 000000000..4aa688a7e --- /dev/null +++ b/samples/tictactoe/app/lint-baseline.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/tictactoe/app/src/androidTest/java/com/squareup/sample/TicTacToeEspressoTest.kt b/samples/tictactoe/app/src/androidTest/java/com/squareup/sample/TicTacToeEspressoTest.kt index 563b7a876..12bd4c2f7 100644 --- a/samples/tictactoe/app/src/androidTest/java/com/squareup/sample/TicTacToeEspressoTest.kt +++ b/samples/tictactoe/app/src/androidTest/java/com/squareup/sample/TicTacToeEspressoTest.kt @@ -19,11 +19,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.sample.mainactivity.TicTacToeActivity import com.squareup.sample.tictactoe.R import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.actuallyPressBack import com.squareup.workflow1.ui.internal.test.inAnyView import com.squareup.workflow1.ui.internal.test.retryBlocking +import leakcanary.DetectLeaksAfterTestSuccess import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.endsWith import org.junit.After diff --git a/samples/tictactoe/app/src/main/AndroidManifest.xml b/samples/tictactoe/app/src/main/AndroidManifest.xml index 24d6db423..caafc96bf 100644 --- a/samples/tictactoe/app/src/main/AndroidManifest.xml +++ b/samples/tictactoe/app/src/main/AndroidManifest.xml @@ -8,7 +8,8 @@ android:allowBackup="true" > - + diff --git a/samples/todo-android/app/lint-baseline.xml b/samples/todo-android/app/lint-baseline.xml new file mode 100644 index 000000000..8d0983b2f --- /dev/null +++ b/samples/todo-android/app/lint-baseline.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/todo-android/app/src/androidTest/java/com/squareup/sample/mainactivity/TodoAppTest.kt b/samples/todo-android/app/src/androidTest/java/com/squareup/sample/mainactivity/TodoAppTest.kt index 1ee097ff8..4f88c6676 100644 --- a/samples/todo-android/app/src/androidTest/java/com/squareup/sample/mainactivity/TodoAppTest.kt +++ b/samples/todo-android/app/src/androidTest/java/com/squareup/sample/mainactivity/TodoAppTest.kt @@ -12,10 +12,10 @@ import androidx.test.uiautomator.UiDevice import com.squareup.sample.todo.R import com.squareup.sample.todo.ToDoActivity import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.actuallyPressBack import com.squareup.workflow1.ui.internal.test.inAnyView +import leakcanary.DetectLeaksAfterTestSuccess import org.hamcrest.Matchers.allOf import org.junit.After import org.junit.Before diff --git a/samples/todo-android/app/src/main/AndroidManifest.xml b/samples/todo-android/app/src/main/AndroidManifest.xml index 73f695f62..e817b0579 100644 --- a/samples/todo-android/app/src/main/AndroidManifest.xml +++ b/samples/todo-android/app/src/main/AndroidManifest.xml @@ -8,7 +8,8 @@ android:allowBackup="true" > - + diff --git a/samples/tutorial/tutorial-1-complete/build.gradle b/samples/tutorial/tutorial-1-complete/build.gradle index c9c7e8b7b..973fa0545 100644 --- a/samples/tutorial/tutorial-1-complete/build.gradle +++ b/samples/tutorial/tutorial-1-complete/build.gradle @@ -4,12 +4,12 @@ plugins { } android { - compileSdk = 32 + compileSdk = 33 defaultConfig { applicationId "com.squareup.workflow.tutorial" minSdk = 21 - targetSdk = 32 + targetSdk = 33 versionCode 1 versionName "1.0" diff --git a/samples/tutorial/tutorial-2-complete/build.gradle b/samples/tutorial/tutorial-2-complete/build.gradle index c9b9f9d05..0e020e74f 100644 --- a/samples/tutorial/tutorial-2-complete/build.gradle +++ b/samples/tutorial/tutorial-2-complete/build.gradle @@ -4,12 +4,12 @@ plugins { } android { - compileSdk = 32 + compileSdk = 33 defaultConfig { applicationId "workflow.tutorial" minSdk = 21 - targetSdk = 32 + targetSdk = 33 versionCode 1 versionName "1.0" diff --git a/samples/tutorial/tutorial-3-complete/build.gradle b/samples/tutorial/tutorial-3-complete/build.gradle index c9b9f9d05..0e020e74f 100644 --- a/samples/tutorial/tutorial-3-complete/build.gradle +++ b/samples/tutorial/tutorial-3-complete/build.gradle @@ -4,12 +4,12 @@ plugins { } android { - compileSdk = 32 + compileSdk = 33 defaultConfig { applicationId "workflow.tutorial" minSdk = 21 - targetSdk = 32 + targetSdk = 33 versionCode 1 versionName "1.0" diff --git a/samples/tutorial/tutorial-4-complete/build.gradle b/samples/tutorial/tutorial-4-complete/build.gradle index c9b9f9d05..0e020e74f 100644 --- a/samples/tutorial/tutorial-4-complete/build.gradle +++ b/samples/tutorial/tutorial-4-complete/build.gradle @@ -4,12 +4,12 @@ plugins { } android { - compileSdk = 32 + compileSdk = 33 defaultConfig { applicationId "workflow.tutorial" minSdk = 21 - targetSdk = 32 + targetSdk = 33 versionCode 1 versionName "1.0" diff --git a/samples/tutorial/tutorial-base/build.gradle b/samples/tutorial/tutorial-base/build.gradle index 4ff9af236..a58e90c29 100644 --- a/samples/tutorial/tutorial-base/build.gradle +++ b/samples/tutorial/tutorial-base/build.gradle @@ -4,12 +4,12 @@ plugins { } android { - compileSdk = 32 + compileSdk = 33 defaultConfig { applicationId "workflow.tutorial" minSdk = 21 - targetSdk = 32 + targetSdk = 33 versionCode 1 versionName "1.0" diff --git a/samples/tutorial/tutorial-final/build.gradle b/samples/tutorial/tutorial-final/build.gradle index 8b8cb1e6f..c18d1f2e7 100644 --- a/samples/tutorial/tutorial-final/build.gradle +++ b/samples/tutorial/tutorial-final/build.gradle @@ -4,12 +4,12 @@ plugins { } android { - compileSdk = 32 + compileSdk = 33 defaultConfig { applicationId "workflow.tutorial" minSdk = 21 - targetSdk = 32 + targetSdk = 33 versionCode 1 versionName "1.0" diff --git a/samples/tutorial/tutorial-views/build.gradle b/samples/tutorial/tutorial-views/build.gradle index 7e94a6169..d680733b4 100644 --- a/samples/tutorial/tutorial-views/build.gradle +++ b/samples/tutorial/tutorial-views/build.gradle @@ -4,11 +4,11 @@ plugins { } android { - compileSdk = 32 + compileSdk = 33 defaultConfig { minSdk = 21 - targetSdk = 32 + targetSdk = 33 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/workflow-ui/compose-tooling/build.gradle.kts b/workflow-ui/compose-tooling/build.gradle.kts index bf7e67d62..dbaca35e1 100644 --- a/workflow-ui/compose-tooling/build.gradle.kts +++ b/workflow-ui/compose-tooling/build.gradle.kts @@ -26,7 +26,10 @@ tasks.withType { } dependencies { + val composeBom = platform(libs.androidx.compose.bom) + androidTestImplementation(libs.androidx.activity.core) + androidTestImplementation(composeBom) androidTestImplementation(libs.androidx.compose.ui.test.junit4) androidTestImplementation(libs.androidx.test.core) androidTestImplementation(libs.androidx.test.truth) @@ -34,6 +37,7 @@ dependencies { androidTestImplementation(project(":workflow-runtime")) + implementation(composeBom) implementation(libs.androidx.compose.foundation) implementation(libs.androidx.compose.foundation.layout) implementation(libs.androidx.compose.runtime) diff --git a/workflow-ui/compose-tooling/dependencies/releaseRuntimeClasspath.txt b/workflow-ui/compose-tooling/dependencies/releaseRuntimeClasspath.txt index 4b28918d4..1cf9fe400 100644 --- a/workflow-ui/compose-tooling/dependencies/releaseRuntimeClasspath.txt +++ b/workflow-ui/compose-tooling/dependencies/releaseRuntimeClasspath.txt @@ -3,41 +3,48 @@ :workflow-ui:compose :workflow-ui:core-android :workflow-ui:core-common -androidx.activity:activity:1.3.0 +androidx.activity:activity-ktx:1.6.1 +androidx.activity:activity:1.6.1 androidx.annotation:annotation-experimental:1.1.0 -androidx.annotation:annotation:1.2.0 +androidx.annotation:annotation:1.5.0 androidx.arch.core:core-common:2.1.0 androidx.arch.core:core-runtime:2.1.0 androidx.autofill:autofill:1.0.0 androidx.collection:collection:1.1.0 -androidx.compose.animation:animation-core:1.1.0-rc01 -androidx.compose.animation:animation:1.1.0-rc01 -androidx.compose.foundation:foundation-layout:1.1.0-rc01 -androidx.compose.foundation:foundation:1.1.0-rc01 -androidx.compose.runtime:runtime-saveable:1.1.0-rc01 -androidx.compose.runtime:runtime:1.1.0-rc01 -androidx.compose.ui:ui-geometry:1.1.0-rc01 -androidx.compose.ui:ui-graphics:1.1.0-rc01 -androidx.compose.ui:ui-text:1.1.0-rc01 -androidx.compose.ui:ui-tooling-preview:1.1.0-rc01 -androidx.compose.ui:ui-unit:1.1.0-rc01 -androidx.compose.ui:ui-util:1.1.0-rc01 -androidx.compose.ui:ui:1.1.0-rc01 +androidx.compose.animation:animation-core:1.3.3 +androidx.compose.animation:animation:1.3.3 +androidx.compose.foundation:foundation-layout:1.3.1 +androidx.compose.foundation:foundation:1.3.1 +androidx.compose.runtime:runtime-saveable:1.3.3 +androidx.compose.runtime:runtime:1.3.3 +androidx.compose.ui:ui-geometry:1.3.3 +androidx.compose.ui:ui-graphics:1.3.3 +androidx.compose.ui:ui-text:1.3.3 +androidx.compose.ui:ui-tooling-preview:1.3.3 +androidx.compose.ui:ui-unit:1.3.3 +androidx.compose.ui:ui-util:1.3.3 +androidx.compose.ui:ui:1.3.3 +androidx.compose:compose-bom:2023.01.00 +androidx.concurrent:concurrent-futures:1.0.0 androidx.core:core-ktx:1.6.0 -androidx.core:core:1.6.0 -androidx.lifecycle:lifecycle-common-java8:2.4.0 -androidx.lifecycle:lifecycle-common:2.4.0 -androidx.lifecycle:lifecycle-livedata-core:2.4.0 -androidx.lifecycle:lifecycle-runtime-ktx:2.4.0 -androidx.lifecycle:lifecycle-runtime:2.4.0 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0 -androidx.lifecycle:lifecycle-viewmodel:2.4.0 -androidx.profileinstaller:profileinstaller:1.1.0-rc01 -androidx.savedstate:savedstate:1.1.0 -androidx.startup:startup-runtime:1.0.0 +androidx.core:core:1.8.0 +androidx.customview:customview-poolingcontainer:1.0.0 +androidx.lifecycle:lifecycle-common-java8:2.5.1 +androidx.lifecycle:lifecycle-common:2.5.1 +androidx.lifecycle:lifecycle-livedata-core:2.5.1 +androidx.lifecycle:lifecycle-runtime-ktx:2.5.1 +androidx.lifecycle:lifecycle-runtime:2.5.1 +androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1 +androidx.lifecycle:lifecycle-viewmodel:2.5.1 +androidx.profileinstaller:profileinstaller:1.2.0 +androidx.savedstate:savedstate-ktx:1.2.0 +androidx.savedstate:savedstate:1.2.0 +androidx.startup:startup-runtime:1.1.1 androidx.tracing:tracing:1.0.0 androidx.transition:transition:1.4.1 androidx.versionedparcelable:versionedparcelable:1.1.1 +com.google.guava:listenablefuture:1.0 com.squareup.okio:okio-jvm:3.0.0 com.squareup.okio:okio:3.0.0 org.jetbrains.kotlin:kotlin-bom:1.7.20 diff --git a/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/LegacyPreviewViewFactoryTest.kt b/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/LegacyPreviewViewFactoryTest.kt index 4d43c9060..6338b6c94 100644 --- a/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/LegacyPreviewViewFactoryTest.kt +++ b/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/LegacyPreviewViewFactoryTest.kt @@ -18,9 +18,9 @@ import com.squareup.workflow1.ui.ViewEnvironmentKey import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.compose.WorkflowRendering import com.squareup.workflow1.ui.compose.composeViewFactory -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/PreviewViewFactoryTest.kt b/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/PreviewViewFactoryTest.kt index 2af1f0c58..bba768ca6 100644 --- a/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/PreviewViewFactoryTest.kt +++ b/workflow-ui/compose-tooling/src/androidTest/java/com/squareup/workflow1/ui/compose/tooling/PreviewViewFactoryTest.kt @@ -19,9 +19,9 @@ import com.squareup.workflow1.ui.ViewEnvironmentKey import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.compose.WorkflowRendering import com.squareup.workflow1.ui.compose.composeScreenViewFactory -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/workflow-ui/compose/README.md b/workflow-ui/compose/README.md index 558c1db31..4f2c8b6b2 100644 --- a/workflow-ui/compose/README.md +++ b/workflow-ui/compose/README.md @@ -36,9 +36,9 @@ Workflow isn’t just a state management library — Workflow UI includes naviga Because these containers define “lifecycles” for parts of the UI, they need to communicate that to the Compose primitives through the AndroidX concepts of [`LifecycleOwner`](https://developer.android.com/reference/androidx/lifecycle/LifecycleOwner) and [`SavedStateRegistry`](https://developer.android.com/reference/kotlin/androidx/savedstate/SavedStateRegistry). When a composition is hosted inside an Android `View`, the [`AbstractComposeView`](https://developer.android.com/reference/kotlin/androidx/compose/ui/platform/AbstractComposeView) that bridges the two reads the [`ViewTreeLifecycleOwner`](https://developer.android.com/reference/androidx/lifecycle/ViewTreeLifecycleOwner) to find the nearest `Lifecycle` responsible for that view. -> AndroidX recently introduced the concept of `ViewTree*` helpers — these are static getters and setters that set `View` tags, and search up the view hierarchy for those tags, to allow views to communicate in an ambient way with their children. [`ViewTreeLifecycleOwner`](https://developer.android.com/reference/androidx/lifecycle/ViewTreeLifecycleOwner), for example, allows any view to find the nearest `LifecycleOwner` by looking up the `View` tree. AndroidX `Fragment`s and `ComponentActivity`s set the `ViewTreeLifecycleOwner`, `ViewTreeSavedStateRegistry`, and other owners on their root views to support this. +> AndroidX recently introduced the concept of `ViewTree*` helpers — these are static getters and setters that set `View` tags, and search up the view hierarchy for those tags, to allow views to communicate in an ambient way with their children. [`ViewTreeLifecycleOwner`](https://developer.android.com/reference/androidx/lifecycle/ViewTreeLifecycleOwner), for example, allows any view to find the nearest `LifecycleOwner` by looking up the `View` tree. AndroidX `Fragment`s and `ComponentActivity`s set the `ViewTreeLifecycleOwner`, `SavedStateRegistry`, and other owners on their root views to support this. -The `Lifecycle` is then observed, both to know when it is safe to restore state, and to know when to dispose the composition because the navigation element is going away. The view also reads the [`ViewTreeSavedStateRegistry`](https://developer.android.com/reference/androidx/savedstate/ViewTreeSavedStateRegistryOwner), wraps it in a [`SaveableStateRegistry`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/SaveableStateRegistry), and provides it to the composition via the `LocalSaveableStateRegistry`. As per the [`SavedStateRegistry` contract](https://developer.android.com/reference/androidx/savedstate/SavedStateRegistry#consumeRestoredStateForKey(java.lang.String)), the registry is asked to restore the composition state as soon as the `Lifecycle` moves to the `CREATED` state. Any `rememberSaveable` calls in the composition will use this mechanism to save and restore their state. +The `Lifecycle` is then observed, both to know when it is safe to restore state, and to know when to dispose the composition because the navigation element is going away. The view also reads the [`SavedStateRegistry`](https://developer.android.com/reference/androidx/savedstate/SavedStateRegistryOwner), wraps it in a [`SaveableStateRegistry`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/saveable/SaveableStateRegistry), and provides it to the composition via the `LocalSaveableStateRegistry`. As per the [`SavedStateRegistry` contract](https://developer.android.com/reference/androidx/savedstate/SavedStateRegistry#consumeRestoredStateForKey(java.lang.String)), the registry is asked to restore the composition state as soon as the `Lifecycle` moves to the `CREATED` state. Any `rememberSaveable` calls in the composition will use this mechanism to save and restore their state. In order for this wiring to all work with Workflows, the Workflow navigation containers must correctly publish `Lifecycle`s and `SavedStateRegistry`s for their child views. The container already manages state saving and restoration via the Android `View` “hierarchy state” mechanism that all `View` classes participate in, so it’s not much of a stretch for them to support this new AndroidX stuff as well. The tricky part is that the sequencing of these different state mechanisms is picky and a little complicated, and we ideally want the Workflow code to support this stuff even if the Workflow view root is hosted in an environment that doesn’t (e.g. a non-AndroidX `Activity`). @@ -68,7 +68,7 @@ The old mechanism depends on `View`s having their IDs set. These IDs are used to Here’s how the view restoration system works with AndroidX’s `SavedStateRegistry`: `View` is instantiated. Because the view hasn’t been attached to a parent yet, it can’t use the `ViewTree*Owner` functions. -`View` is eventually added to a `ViewGroup`, and attached to the window. Now the view has a parent, so the `onAttached` callback can search up the tree for the `ViewTreeLifecycleOwner`. It also looks for the `ViewTreeSavedStateRegistryOwner` — it can’t use it yet though. +`View` is eventually added to a `ViewGroup`, and attached to the window. Now the view has a parent, so the `onAttached` callback can search up the tree for the `ViewTreeLifecycleOwner`. It also looks for the `SavedStateRegistryOwner` — it can’t use it yet though. One or more `SavedStateProvider`s are registered on the registry associated with arbitrary string keys — these providers are simply functions that will be called arbitrarily-many times to provide saved values when the system needs to save view state. The `Lifecycle` is observed, as long as the view remains attached. When the lifecycle state moves to `CREATED`, the `SavedStateRegistry` can be queried. The view’s initialization logic can now call `consumeRestoredStateForKey` to read back any previously-saved values associated with string keys. If there were no values available, null will be returned and the view should fallback to some default value. @@ -78,7 +78,7 @@ Note the difference in when the restoration happens relative to the lifecycle st | Instance state | `SavedStateRegistry` | |-------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| All views participate in this mechanism, and can’t opt-out. | Views must opt-in by getting access to a `SavedStateRegistry` either via a `ViewTreeSavedStateRegistry` or some other way. | +| All views participate in this mechanism, and can’t opt-out. | Views must opt-in by getting access to a `SavedStateRegistry` either via `findViewTreeSavedStateRegistryOwner` or some other way. | | Save/restore callbacks are built into the `View` base class as overridable methods. _Restoration is “push-based” — views are told when to restore themselves._ | Views must register saved state providers explicitly in order to get the save callbacks. _Restoration is “pull-based” — views must request previously-saved values at the appropriate time. Registry API requires coordination with Lifecycle API._ | | Restored after lifecycle `STARTED`. | Restored after lifecycle `CREATED`. | | Identified by hierarchy of view IDs. | Identified by string keys. | diff --git a/workflow-ui/compose/build.gradle.kts b/workflow-ui/compose/build.gradle.kts index 7d37d9de0..b880961f0 100644 --- a/workflow-ui/compose/build.gradle.kts +++ b/workflow-ui/compose/build.gradle.kts @@ -27,7 +27,10 @@ tasks.withType { } dependencies { + val composeBom = platform(libs.androidx.compose.bom) + androidTestImplementation(libs.androidx.activity.core) + androidTestImplementation(composeBom) androidTestImplementation(libs.androidx.compose.foundation) androidTestImplementation(libs.androidx.compose.ui.test.junit4) androidTestImplementation(libs.androidx.test.core) @@ -43,6 +46,7 @@ dependencies { api(project(":workflow-ui:core-android")) api(project(":workflow-ui:core-common")) + implementation(composeBom) implementation(libs.androidx.compose.foundation.layout) implementation(libs.androidx.compose.runtime.saveable) implementation(libs.androidx.compose.ui) diff --git a/workflow-ui/compose/dependencies/releaseRuntimeClasspath.txt b/workflow-ui/compose/dependencies/releaseRuntimeClasspath.txt index 7b6d47835..446563ec1 100644 --- a/workflow-ui/compose/dependencies/releaseRuntimeClasspath.txt +++ b/workflow-ui/compose/dependencies/releaseRuntimeClasspath.txt @@ -2,37 +2,45 @@ :workflow-runtime :workflow-ui:core-android :workflow-ui:core-common -androidx.activity:activity:1.3.0 +androidx.activity:activity-ktx:1.6.1 +androidx.activity:activity:1.6.1 androidx.annotation:annotation-experimental:1.1.0 -androidx.annotation:annotation:1.2.0 +androidx.annotation:annotation:1.5.0 androidx.arch.core:core-common:2.1.0 androidx.arch.core:core-runtime:2.1.0 androidx.autofill:autofill:1.0.0 androidx.collection:collection:1.1.0 -androidx.compose.foundation:foundation-layout:1.1.0-rc01 -androidx.compose.runtime:runtime-saveable:1.1.0-rc01 -androidx.compose.runtime:runtime:1.1.0-rc01 -androidx.compose.ui:ui-geometry:1.1.0-rc01 -androidx.compose.ui:ui-graphics:1.1.0-rc01 -androidx.compose.ui:ui-text:1.1.0-rc01 -androidx.compose.ui:ui-unit:1.1.0-rc01 -androidx.compose.ui:ui-util:1.1.0-rc01 -androidx.compose.ui:ui:1.1.0-rc01 +androidx.compose.animation:animation-core:1.3.3 +androidx.compose.foundation:foundation-layout:1.3.1 +androidx.compose.runtime:runtime-saveable:1.3.3 +androidx.compose.runtime:runtime:1.3.3 +androidx.compose.ui:ui-geometry:1.3.3 +androidx.compose.ui:ui-graphics:1.3.3 +androidx.compose.ui:ui-text:1.3.3 +androidx.compose.ui:ui-unit:1.3.3 +androidx.compose.ui:ui-util:1.3.3 +androidx.compose.ui:ui:1.3.3 +androidx.compose:compose-bom:2023.01.00 +androidx.concurrent:concurrent-futures:1.0.0 androidx.core:core-ktx:1.6.0 -androidx.core:core:1.6.0 -androidx.lifecycle:lifecycle-common-java8:2.4.0 -androidx.lifecycle:lifecycle-common:2.4.0 -androidx.lifecycle:lifecycle-livedata-core:2.4.0 -androidx.lifecycle:lifecycle-runtime-ktx:2.4.0 -androidx.lifecycle:lifecycle-runtime:2.4.0 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0 -androidx.lifecycle:lifecycle-viewmodel:2.4.0 -androidx.profileinstaller:profileinstaller:1.1.0-rc01 -androidx.savedstate:savedstate:1.1.0 -androidx.startup:startup-runtime:1.0.0 +androidx.core:core:1.8.0 +androidx.customview:customview-poolingcontainer:1.0.0 +androidx.lifecycle:lifecycle-common-java8:2.5.1 +androidx.lifecycle:lifecycle-common:2.5.1 +androidx.lifecycle:lifecycle-livedata-core:2.5.1 +androidx.lifecycle:lifecycle-runtime-ktx:2.5.1 +androidx.lifecycle:lifecycle-runtime:2.5.1 +androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1 +androidx.lifecycle:lifecycle-viewmodel:2.5.1 +androidx.profileinstaller:profileinstaller:1.2.0 +androidx.savedstate:savedstate-ktx:1.2.0 +androidx.savedstate:savedstate:1.2.0 +androidx.startup:startup-runtime:1.1.1 androidx.tracing:tracing:1.0.0 androidx.transition:transition:1.4.1 androidx.versionedparcelable:versionedparcelable:1.1.1 +com.google.guava:listenablefuture:1.0 com.squareup.okio:okio-jvm:3.0.0 com.squareup.okio:okio:3.0.0 org.jetbrains.kotlin:kotlin-bom:1.7.20 diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewFactoryTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewFactoryTest.kt index af9f0d181..6f1aae60a 100644 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewFactoryTest.kt +++ b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewFactoryTest.kt @@ -22,10 +22,10 @@ import com.squareup.workflow1.ui.ViewEnvironmentKey import com.squareup.workflow1.ui.ViewRegistry import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.WorkflowViewStub -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.plus +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewTreeIntegrationTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewTreeIntegrationTest.kt index ce8d343de..5f7711963 100644 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewTreeIntegrationTest.kt +++ b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/ComposeViewTreeIntegrationTest.kt @@ -37,11 +37,11 @@ import com.squareup.workflow1.ui.container.BackStackScreen import com.squareup.workflow1.ui.container.BodyAndOverlaysScreen import com.squareup.workflow1.ui.container.ScreenOverlay import com.squareup.workflow1.ui.container.ScreenOverlayDialogFactory -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.WorkflowUiTestActivity import com.squareup.workflow1.ui.plus +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Before import org.junit.Rule import org.junit.Test diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/CompositionRootTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/CompositionRootTest.kt index abd78f668..bce16d6a7 100644 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/CompositionRootTest.kt +++ b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/CompositionRootTest.kt @@ -8,9 +8,9 @@ import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.test.ext.junit.runners.AndroidJUnit4 import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewFactoryTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewFactoryTest.kt index 1daec571b..8ee54914f 100644 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewFactoryTest.kt +++ b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewFactoryTest.kt @@ -23,10 +23,10 @@ import com.squareup.workflow1.ui.ViewEnvironmentKey import com.squareup.workflow1.ui.ViewRegistry import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.WorkflowViewStub -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.plus +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewTreeIntegrationTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewTreeIntegrationTest.kt index f9f476889..3120134bd 100644 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewTreeIntegrationTest.kt +++ b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyComposeViewTreeIntegrationTest.kt @@ -37,12 +37,12 @@ import com.squareup.workflow1.ui.ViewRegistry import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.backstack.BackStackScreen import com.squareup.workflow1.ui.bindShowRendering -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.internal.test.LegacyWorkflowUiTestActivity import com.squareup.workflow1.ui.modal.HasModals import com.squareup.workflow1.ui.modal.ModalViewContainer +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Before import org.junit.Rule import org.junit.Test diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyWorkflowRenderingTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyWorkflowRenderingTest.kt index a465216e1..89e5a36f1 100644 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyWorkflowRenderingTest.kt +++ b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/LegacyWorkflowRenderingTest.kt @@ -67,10 +67,10 @@ import com.squareup.workflow1.ui.ViewFactory import com.squareup.workflow1.ui.ViewRegistry import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.bindShowRendering -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.plus +import leakcanary.DetectLeaksAfterTestSuccess import org.hamcrest.Description import org.hamcrest.TypeSafeMatcher import org.junit.Rule @@ -322,6 +322,8 @@ internal class LegacyWorkflowRenderingTest { override fun getLifecycle(): Lifecycle = registry } composeRule.runOnIdle { + // Cannot go directly to DESTROYED + parentOwner.registry.currentState = CREATED parentOwner.registry.currentState = DESTROYED } @@ -539,7 +541,6 @@ internal class LegacyWorkflowRenderingTest { } } - @Suppress("UNCHECKED_CAST") private interface ComposableRendering> : AndroidViewRendering { diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/RenderAsStateTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/RenderAsStateTest.kt index 2b29f540c..e060be47e 100644 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/RenderAsStateTest.kt +++ b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/RenderAsStateTest.kt @@ -24,7 +24,6 @@ import com.squareup.workflow1.rendering import com.squareup.workflow1.stateless import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import com.squareup.workflow1.ui.compose.RenderAsStateTest.SnapshottingWorkflow.SnapshottedRendering -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.writeUtf8WithLength @@ -37,6 +36,7 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest +import leakcanary.DetectLeaksAfterTestSuccess import okio.ByteString import okio.ByteString.Companion.decodeBase64 import org.junit.Rule diff --git a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/WorkflowRenderingTest.kt b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/WorkflowRenderingTest.kt index acd205fd8..a858d5b7b 100644 --- a/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/WorkflowRenderingTest.kt +++ b/workflow-ui/compose/src/androidTest/java/com/squareup/workflow1/ui/compose/WorkflowRenderingTest.kt @@ -65,10 +65,10 @@ import com.squareup.workflow1.ui.ScreenViewHolder import com.squareup.workflow1.ui.ViewEnvironment import com.squareup.workflow1.ui.ViewRegistry import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdleAfterTestRule import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.plus +import leakcanary.DetectLeaksAfterTestSuccess import org.hamcrest.Description import org.hamcrest.TypeSafeMatcher import org.junit.Rule @@ -318,6 +318,8 @@ internal class WorkflowRenderingTest { override fun getLifecycle(): Lifecycle = registry } composeRule.runOnIdle { + // Cannot go directly to DESTROYED + parentOwner.registry.currentState = CREATED parentOwner.registry.currentState = DESTROYED } diff --git a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/CompositionRoot.kt b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/CompositionRoot.kt index dcd725d18..35e3f54ba 100644 --- a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/CompositionRoot.kt +++ b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/CompositionRoot.kt @@ -3,7 +3,7 @@ package com.squareup.workflow1.ui.compose import androidx.annotation.VisibleForTesting -import androidx.annotation.VisibleForTesting.PRIVATE +import androidx.annotation.VisibleForTesting.Companion.PRIVATE import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.staticCompositionLocalOf diff --git a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/RenderAsState.kt b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/RenderAsState.kt index 7bc5e41b1..0e5d53732 100644 --- a/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/RenderAsState.kt +++ b/workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/RenderAsState.kt @@ -23,12 +23,12 @@ import com.squareup.workflow1.WorkflowInterceptor import com.squareup.workflow1.renderWorkflowIn import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart.UNDISPATCHED import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import kotlinx.coroutines.plus import okio.ByteString @@ -206,18 +206,23 @@ private class WorkflowRuntimeState( onOutput = onOutput ) - renderings - .onEach { - renderingState.value = it.rendering - snapshotState.value = it.snapshot - } + workflowScope.launch( + start = UNDISPATCHED, + context = Dispatchers.Unconfined + ) { // We collect the renderings in the workflowScope to participate in structured concurrency, // however we don't need to use its dispatcher – this collector is simply setting snapshot // state values, which is thread safe. // Also, if the scope uses a non-immediate dispatcher, the initial states won't get set until // the dispatcher dispatches the collection coroutine, but our contract requires them to be - // set by the time this function returns and using the Unconfined dispatcher guarantees that. - .launchIn(workflowScope + Dispatchers.Unconfined) + // set by the time this function returns and using the Unconfined dispatcher along with + // launching this coroutine as CoroutineStart.UNDISPATCHED guarantees that. + + renderings.collect { + renderingState.value = it.rendering + snapshotState.value = it.snapshot + } + } } fun setProps(props: PropsT) { diff --git a/workflow-ui/container-android/dependencies/releaseRuntimeClasspath.txt b/workflow-ui/container-android/dependencies/releaseRuntimeClasspath.txt index 808d6b0c0..f15e3dad9 100644 --- a/workflow-ui/container-android/dependencies/releaseRuntimeClasspath.txt +++ b/workflow-ui/container-android/dependencies/releaseRuntimeClasspath.txt @@ -3,36 +3,43 @@ :workflow-ui:container-common :workflow-ui:core-android :workflow-ui:core-common -androidx.activity:activity:1.3.0 -androidx.annotation:annotation-experimental:1.1.0 -androidx.annotation:annotation:1.2.0 -androidx.appcompat:appcompat-resources:1.3.1 -androidx.appcompat:appcompat:1.3.1 +androidx.activity:activity:1.6.1 +androidx.annotation:annotation-experimental:1.3.0 +androidx.annotation:annotation:1.3.0 +androidx.appcompat:appcompat-resources:1.6.1 +androidx.appcompat:appcompat:1.6.1 androidx.arch.core:core-common:2.1.0 androidx.arch.core:core-runtime:2.1.0 androidx.collection:collection:1.1.0 -androidx.core:core-ktx:1.6.0 -androidx.core:core:1.6.0 +androidx.concurrent:concurrent-futures:1.0.0 +androidx.core:core-ktx:1.9.0 +androidx.core:core:1.9.0 androidx.cursoradapter:cursoradapter:1.0.0 androidx.customview:customview:1.0.0 androidx.drawerlayout:drawerlayout:1.0.0 +androidx.emoji2:emoji2-views-helper:1.2.0 +androidx.emoji2:emoji2:1.2.0 androidx.fragment:fragment:1.3.6 androidx.interpolator:interpolator:1.0.0 -androidx.lifecycle:lifecycle-common:2.4.0 -androidx.lifecycle:lifecycle-livedata-core:2.4.0 -androidx.lifecycle:lifecycle-livedata:2.4.0 -androidx.lifecycle:lifecycle-runtime-ktx:2.4.0 -androidx.lifecycle:lifecycle-runtime:2.4.0 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0 -androidx.lifecycle:lifecycle-viewmodel:2.4.0 +androidx.lifecycle:lifecycle-common:2.5.1 +androidx.lifecycle:lifecycle-livedata-core:2.5.1 +androidx.lifecycle:lifecycle-livedata:2.5.1 +androidx.lifecycle:lifecycle-process:2.5.1 +androidx.lifecycle:lifecycle-runtime-ktx:2.5.1 +androidx.lifecycle:lifecycle-runtime:2.5.1 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1 +androidx.lifecycle:lifecycle-viewmodel:2.5.1 androidx.loader:loader:1.0.0 -androidx.savedstate:savedstate:1.1.0 +androidx.resourceinspection:resourceinspection-annotation:1.0.1 +androidx.savedstate:savedstate:1.2.0 +androidx.startup:startup-runtime:1.1.1 androidx.tracing:tracing:1.0.0 androidx.transition:transition:1.4.1 androidx.vectordrawable:vectordrawable-animated:1.1.0 androidx.vectordrawable:vectordrawable:1.1.0 androidx.versionedparcelable:versionedparcelable:1.1.1 androidx.viewpager:viewpager:1.0.0 +com.google.guava:listenablefuture:1.0 com.squareup.okio:okio-jvm:3.0.0 com.squareup.okio:okio:3.0.0 org.jetbrains.kotlin:kotlin-bom:1.7.20 diff --git a/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/modal/test/ModalViewContainerLifecycleTest.kt b/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/modal/test/ModalViewContainerLifecycleTest.kt index 25f3aa6bf..6b871edeb 100644 --- a/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/modal/test/ModalViewContainerLifecycleTest.kt +++ b/workflow-ui/container-android/src/androidTest/java/com/squareup/workflow1/ui/modal/test/ModalViewContainerLifecycleTest.kt @@ -8,11 +8,11 @@ import androidx.lifecycle.LifecycleOwner import androidx.test.ext.junit.rules.ActivityScenarioRule import com.google.common.truth.Truth.assertThat import com.squareup.workflow1.ui.WorkflowUiExperimentalApi -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule import com.squareup.workflow1.ui.modal.ModalViewContainer import com.squareup.workflow1.ui.modal.test.ModalViewContainerLifecycleActivity.TestRendering.LeafRendering import com.squareup.workflow1.ui.modal.test.ModalViewContainerLifecycleActivity.TestRendering.RecurseRendering +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/ModalContainer.kt b/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/ModalContainer.kt index 8e814b36c..a28b2c572 100644 --- a/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/ModalContainer.kt +++ b/workflow-ui/container-android/src/main/java/com/squareup/workflow1/ui/modal/ModalContainer.kt @@ -57,8 +57,8 @@ public abstract class ModalContainer @JvmOverloads constr } /** - * Provides a new `ViewTreeSavedStateRegistryOwner` for each dialog, - * which will save to the `ViewTreeSavedStateRegistryOwner` of this container view. + * Provides a new `SavedStateRegistryOwner` for each dialog, + * which will save to the `SavedStateRegistryOwner` of this container view. */ private val stateRegistryAggregator = WorkflowSavedStateRegistryAggregator() @@ -90,7 +90,7 @@ public abstract class ModalContainer @JvmOverloads constr dialogView, findParentLifecycle = { parentLifecycleOwner.lifecycle } ) - // Ensure that each dialog has its own ViewTreeSavedStateRegistryOwner, + // Ensure that each dialog has its own SavedStateRegistryOwner, // so views in each dialog layer don't clash with other layers. stateRegistryAggregator.installChildRegistryOwnerOn( view = dialogView, @@ -213,7 +213,7 @@ public abstract class ModalContainer @JvmOverloads constr public val extra: Any? = null ) { /** - * The unique id of the `ViewTreeSavedStateRegistryOwner` that will be placed + * The unique id of the `SavedStateRegistryOwner` that will be placed * on the dialog's decor view by [stateRegistryAggregator]. */ internal lateinit var savedStateRegistryKey: String diff --git a/workflow-ui/core-android/build.gradle.kts b/workflow-ui/core-android/build.gradle.kts index f1b403931..5b16b2308 100644 --- a/workflow-ui/core-android/build.gradle.kts +++ b/workflow-ui/core-android/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { implementation(libs.androidx.lifecycle.core) implementation(libs.androidx.lifecycle.ktx) implementation(libs.androidx.lifecycle.viewmodel.savedstate) + implementation(libs.androidx.savedstate) implementation(libs.androidx.transition) implementation(libs.kotlinx.coroutines.android) implementation(libs.kotlinx.coroutines.core) diff --git a/workflow-ui/core-android/dependencies/releaseRuntimeClasspath.txt b/workflow-ui/core-android/dependencies/releaseRuntimeClasspath.txt index c140250a8..b348ba0ef 100644 --- a/workflow-ui/core-android/dependencies/releaseRuntimeClasspath.txt +++ b/workflow-ui/core-android/dependencies/releaseRuntimeClasspath.txt @@ -1,24 +1,26 @@ :workflow-core :workflow-runtime :workflow-ui:core-common -androidx.activity:activity:1.3.0 +androidx.activity:activity:1.6.1 androidx.annotation:annotation-experimental:1.1.0 androidx.annotation:annotation:1.2.0 androidx.arch.core:core-common:2.1.0 androidx.arch.core:core-runtime:2.1.0 androidx.collection:collection:1.1.0 +androidx.concurrent:concurrent-futures:1.0.0 androidx.core:core-ktx:1.6.0 -androidx.core:core:1.6.0 -androidx.lifecycle:lifecycle-common:2.4.0 -androidx.lifecycle:lifecycle-livedata-core:2.4.0 -androidx.lifecycle:lifecycle-runtime-ktx:2.4.0 -androidx.lifecycle:lifecycle-runtime:2.4.0 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0 -androidx.lifecycle:lifecycle-viewmodel:2.4.0 -androidx.savedstate:savedstate:1.1.0 +androidx.core:core:1.8.0 +androidx.lifecycle:lifecycle-common:2.5.1 +androidx.lifecycle:lifecycle-livedata-core:2.5.1 +androidx.lifecycle:lifecycle-runtime-ktx:2.5.1 +androidx.lifecycle:lifecycle-runtime:2.5.1 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1 +androidx.lifecycle:lifecycle-viewmodel:2.5.1 +androidx.savedstate:savedstate:1.2.0 androidx.tracing:tracing:1.0.0 androidx.transition:transition:1.4.1 androidx.versionedparcelable:versionedparcelable:1.1.1 +com.google.guava:listenablefuture:1.0 com.squareup.okio:okio-jvm:3.0.0 com.squareup.okio:okio:3.0.0 org.jetbrains.kotlin:kotlin-bom:1.7.20 diff --git a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/BackPressedHandlerTest.kt b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/BackPressedHandlerTest.kt index c40530dc1..62ec7f962 100644 --- a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/BackPressedHandlerTest.kt +++ b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/BackPressedHandlerTest.kt @@ -11,8 +11,8 @@ import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.ViewTreeLifecycleOwner import androidx.test.ext.junit.rules.ActivityScenarioRule import com.google.common.truth.Truth.assertThat -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/WorkflowViewStubLifecycleTest.kt b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/WorkflowViewStubLifecycleTest.kt index a9bfffa7f..c8a842bb6 100644 --- a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/WorkflowViewStubLifecycleTest.kt +++ b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/WorkflowViewStubLifecycleTest.kt @@ -17,7 +17,8 @@ import androidx.lifecycle.LifecycleRegistry import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner -import androidx.savedstate.ViewTreeSavedStateRegistryOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches @@ -30,8 +31,8 @@ import com.squareup.workflow1.ui.WorkflowViewStubLifecycleActivity.TestRendering import com.squareup.workflow1.ui.WorkflowViewStubLifecycleActivity.TestRendering.LeafRendering import com.squareup.workflow1.ui.WorkflowViewStubLifecycleActivity.TestRendering.RecurseRendering import com.squareup.workflow1.ui.WorkflowViewStubLifecycleActivity.TestRendering.ViewRendering -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.hamcrest.Matchers.equalTo import org.junit.Rule import org.junit.Test @@ -258,14 +259,16 @@ internal class WorkflowViewStubLifecycleTest { val expectedRegistryOwner = object : SavedStateRegistryOwner { private val controller = SavedStateRegistryController.create(this) private val lifecycleRegistry = LifecycleRegistry(this) + override val savedStateRegistry: SavedStateRegistry + get() = controller.savedStateRegistry + override fun getLifecycle(): Lifecycle = lifecycleRegistry - override fun getSavedStateRegistry(): SavedStateRegistry = controller.savedStateRegistry } data class RegistrySetter(val wrapped: TestRendering) : ViewRendering() { override val viewFactory = fromCode { _, initialEnvironment, context, _ -> val stub = WorkflowViewStub(context) - ViewTreeSavedStateRegistryOwner.set(stub, expectedRegistryOwner) + stub.setViewTreeSavedStateRegistryOwner(expectedRegistryOwner) val frame = FrameLayout(context).apply { addView(stub) } ScreenViewHolder(initialEnvironment, frame) { rendering, viewEnvironment -> @@ -279,7 +282,7 @@ internal class WorkflowViewStubLifecycleTest { it.update( RegistrySetter( CounterRendering("initial") { view -> - initialRegistryOwner = ViewTreeSavedStateRegistryOwner.get(view) + initialRegistryOwner = view.findViewTreeSavedStateRegistryOwner() } ) ) @@ -292,7 +295,7 @@ internal class WorkflowViewStubLifecycleTest { it.update( RegistrySetter( CounterRendering("second") { view -> - subsequentRegistryOwner = ViewTreeSavedStateRegistryOwner.get(view) + subsequentRegistryOwner = view.findViewTreeSavedStateRegistryOwner() } ) ) @@ -325,7 +328,7 @@ internal class WorkflowViewStubLifecycleTest { override fun onViewAttachedToWindow(v: View) { onViewAttached(this@button) - registryOwner = ViewTreeSavedStateRegistryOwner.get(this@button)!! + registryOwner = this@button.findViewTreeSavedStateRegistryOwner()!! lifecycleObserver = object : LifecycleEventObserver { override fun onStateChanged( source: LifecycleOwner, diff --git a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/BackStackContainerPersistenceLifecycleTest.kt b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/BackStackContainerPersistenceLifecycleTest.kt index de2cd2649..60d69c4a2 100644 --- a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/BackStackContainerPersistenceLifecycleTest.kt +++ b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/BackStackContainerPersistenceLifecycleTest.kt @@ -16,8 +16,8 @@ import com.squareup.workflow1.ui.container.fixtures.BackStackContainerLifecycleA import com.squareup.workflow1.ui.container.fixtures.ViewStateTestView import com.squareup.workflow1.ui.container.fixtures.viewForScreen import com.squareup.workflow1.ui.container.fixtures.waitForScreen -import com.squareup.workflow1.ui.internal.test.DetectLeaksAfterTestSuccess import com.squareup.workflow1.ui.internal.test.IdlingDispatcherRule +import leakcanary.DetectLeaksAfterTestSuccess import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain diff --git a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/BackStackContainerTest.kt b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/BackStackContainerTest.kt index ea7d2e914..ee0192873 100644 --- a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/BackStackContainerTest.kt +++ b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/BackStackContainerTest.kt @@ -1,6 +1,8 @@ package com.squareup.workflow1.ui.container import android.content.Context +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Parcel import android.os.Parcelable import android.text.Editable @@ -71,7 +73,14 @@ internal class BackStackContainerTest { val restoredArray = Parcel.obtain().let { parcel -> parcel.unmarshall(bytes, 0, bytes.size) parcel.setDataPosition(0) - parcel.readSparseArray(this::class.java.classLoader)!!.also { parcel.recycle() } + if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + parcel.readSparseArray(this::class.java.classLoader, Parcelable::class.java)!! + .also { parcel.recycle() } + } else { + @Suppress("DEPRECATION") + parcel.readSparseArray(this::class.java.classLoader)!! + .also { parcel.recycle() } + } } // Create a new BackStackContainer with the same id as the original @@ -157,7 +166,6 @@ internal class BackStackContainerTest { private class VisibleBackStackContainer(context: Context) : BackStackContainer(context) { var transitionCount = 0 - @Suppress("UNCHECKED_CAST") val visibleRendering: Screen get() = (getChildAt(0)?.tag as NamedScreen<*>).wrapped diff --git a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/ViewStateCacheTest.kt b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/ViewStateCacheTest.kt index 99818f3ed..f224b35f4 100644 --- a/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/ViewStateCacheTest.kt +++ b/workflow-ui/core-android/src/androidTest/java/com/squareup/workflow1/ui/container/ViewStateCacheTest.kt @@ -1,5 +1,7 @@ package com.squareup.workflow1.ui.container +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Parcel import android.os.Parcelable import android.util.SparseArray @@ -47,9 +49,17 @@ internal class ViewStateCacheTest { parcel.writeParcelable(cache.save(), 0) parcel.setDataPosition(0) - val restoredCache = parcel.readParcelable( - ViewStateCache.Saved::class.java.classLoader - )!!.let { restoredState -> + val restoredCache = ( + if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + parcel.readParcelable( + ViewStateCache.Saved::class.java.classLoader, + ViewStateCache.Saved::class.java + )!! + } else { + @Suppress("DEPRECATION") + parcel.readParcelable(ViewStateCache.Saved::class.java.classLoader)!! + } + ).let { restoredState -> ViewStateCache().apply { restore(restoredState) } } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/SnapshotParcels.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/SnapshotParcels.kt index 22eb38e8b..4ee8c37f7 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/SnapshotParcels.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/SnapshotParcels.kt @@ -1,5 +1,7 @@ package com.squareup.workflow1.ui +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Parcel import android.os.Parcelable import com.squareup.workflow1.Snapshot @@ -34,7 +36,12 @@ public inline fun ByteString.toParcelable(): T { val byteArray = toByteArray() parcel.unmarshall(byteArray, 0, byteArray.size) parcel.setDataPosition(0) - val rtn = parcel.readParcelable(Snapshot::class.java.classLoader)!! + val rtn = if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + parcel.readParcelable(Snapshot::class.java.classLoader, T::class.java)!! + } else { + @Suppress("DEPRECATION") + parcel.readParcelable(Snapshot::class.java.classLoader)!! + } parcel.recycle() return rtn } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/TreeSnapshotSaver.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/TreeSnapshotSaver.kt index 12c5a7207..4c78a9707 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/TreeSnapshotSaver.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/TreeSnapshotSaver.kt @@ -1,5 +1,7 @@ package com.squareup.workflow1.ui +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Bundle import androidx.savedstate.SavedStateRegistry import com.squareup.workflow1.TreeSnapshot @@ -27,10 +29,18 @@ internal interface TreeSnapshotSaver { fun fromSavedStateRegistry(savedStateRegistry: SavedStateRegistry) = object : TreeSnapshotSaver { override fun consumeSnapshot(): TreeSnapshot? { - return savedStateRegistry - .consumeRestoredStateForKey(BUNDLE_KEY) - ?.getParcelable(BUNDLE_KEY) - ?.snapshot + return if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + savedStateRegistry + .consumeRestoredStateForKey(BUNDLE_KEY) + ?.getParcelable(BUNDLE_KEY, PickledTreesnapshot::class.java) + ?.snapshot + } else { + @Suppress("DEPRECATION") + savedStateRegistry + .consumeRestoredStateForKey(BUNDLE_KEY) + ?.getParcelable(BUNDLE_KEY) + ?.snapshot + } } override fun registerSource(source: HasTreeSnapshot) { diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowLayout.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowLayout.kt index ab25c0338..388194e92 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowLayout.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowLayout.kt @@ -1,6 +1,8 @@ package com.squareup.workflow1.ui import android.content.Context +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Parcel import android.os.Parcelable import android.os.Parcelable.Creator @@ -235,7 +237,12 @@ public class WorkflowLayout( } constructor(source: Parcel) : super(source) { - this.childState = source.readSparseArray(SavedState::class.java.classLoader)!! + this.childState = if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + source.readSparseArray(SavedState::class.java.classLoader, SavedState::class.java)!! + } else { + @Suppress("DEPRECATION") + source.readSparseArray(SavedState::class.java.classLoader)!! + } } val childState: SparseArray @@ -271,12 +278,12 @@ public class WorkflowLayout( val scope = CoroutineScope(Dispatchers.Main.immediate) var job: Job? = null - override fun onViewAttachedToWindow(v: View?) { + override fun onViewAttachedToWindow(v: View) { job = source.onEach { screen -> update(screen) } .launchIn(scope) } - override fun onViewDetachedFromWindow(v: View?) { + override fun onViewDetachedFromWindow(v: View) { job?.cancel() job = null } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewStub.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewStub.kt index 0253ae255..4993a6b7f 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewStub.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/WorkflowViewStub.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") - package com.squareup.workflow1.ui import android.content.Context @@ -8,7 +6,9 @@ import android.util.AttributeSet import android.view.View import android.view.ViewGroup import androidx.annotation.IdRes -import androidx.savedstate.ViewTreeSavedStateRegistryOwner +import androidx.savedstate.SavedStateRegistryOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.squareup.workflow1.ui.androidx.WorkflowLifecycleOwner /** @@ -185,7 +185,6 @@ public class WorkflowViewStub @JvmOverloads constructor( rendering: Any, viewEnvironment: ViewEnvironment ): View { - @Suppress("DEPRECATION") show(asScreen(rendering), viewEnvironment) return holder!!.view } @@ -246,9 +245,9 @@ public class WorkflowViewStub @JvmOverloads constructor( } /** - * If a [ViewTreeSavedStateRegistryOwner] was set on this [WorkflowViewStub], sets that owner on + * If a [SavedStateRegistryOwner] was set on this [WorkflowViewStub], sets that owner on * [newView]. Note that this _only_ copies an owner if it was set _directly_ on this view with - * [ViewTreeSavedStateRegistryOwner.set]. If [ViewTreeSavedStateRegistryOwner.get] would return an + * [setViewTreeSavedStateRegistryOwner]. If [findViewTreeSavedStateRegistryOwner] would return an * owner that was set on a parent view, this method does nothing. * * Must be called before [newView] gets attached to the window. @@ -257,13 +256,15 @@ public class WorkflowViewStub @JvmOverloads constructor( // There's no way to ask for the owner only on this view, without looking up the tree, so // we have to compare the results from searching from this view to searching from our parent // (if we have a parent) to determine if we have our own owner. - val myStateRegistryOwner = ViewTreeSavedStateRegistryOwner.get(this) + val myStateRegistryOwner = this.findViewTreeSavedStateRegistryOwner() val parentStateRegistryOwner = - (this.parent as? ViewGroup)?.let(ViewTreeSavedStateRegistryOwner::get) + (this.parent as? ViewGroup)?.run { + findViewTreeSavedStateRegistryOwner() + } if (myStateRegistryOwner !== parentStateRegistryOwner) { // Someone has set an owner on the stub itself, so we need to also set it on the new // subview. - ViewTreeSavedStateRegistryOwner.set(newView, myStateRegistryOwner) + newView.setViewTreeSavedStateRegistryOwner(myStateRegistryOwner) } } } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/KeyedSavedStateRegistryOwner.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/KeyedSavedStateRegistryOwner.kt index 47953e94a..dc2bb3346 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/KeyedSavedStateRegistryOwner.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/KeyedSavedStateRegistryOwner.kt @@ -4,18 +4,18 @@ import androidx.lifecycle.LifecycleOwner import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner -import androidx.savedstate.ViewTreeSavedStateRegistryOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** * The implementation of [SavedStateRegistryOwner] that should be installed on every immediate * child view of container root views (e.g. content views, e.g. backstack frames) so that when - * a view inside a container calls [ViewTreeSavedStateRegistryOwner.get] on itself, one of these + * a view inside a container calls [findViewTreeSavedStateRegistryOwner] on itself, one of these * is returned. * * The container should use a [WorkflowSavedStateRegistryAggregator] to manage its set of * [KeyedSavedStateRegistryOwner] instances, which will save and restore them - * via its own [ViewTreeSavedStateRegistryOwner]. + * via its own [SavedStateRegistryOwner]. * * To create an instance, call [WorkflowSavedStateRegistryAggregator.installChildRegistryOwnerOn]. * @@ -29,5 +29,6 @@ internal class KeyedSavedStateRegistryOwner internal constructor( lifecycleOwner: LifecycleOwner ) : SavedStateRegistryOwner, LifecycleOwner by lifecycleOwner { internal val controller: SavedStateRegistryController = SavedStateRegistryController.create(this) - override fun getSavedStateRegistry(): SavedStateRegistry = controller.savedStateRegistry + override val savedStateRegistry: SavedStateRegistry + get() = controller.savedStateRegistry } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/WorkflowAndroidXSupport.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/WorkflowAndroidXSupport.kt index a0c25d73b..347b2af8d 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/WorkflowAndroidXSupport.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/WorkflowAndroidXSupport.kt @@ -6,7 +6,7 @@ import android.view.View import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewTreeLifecycleOwner import androidx.savedstate.SavedStateRegistryOwner -import androidx.savedstate.ViewTreeSavedStateRegistryOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner import com.squareup.workflow1.ui.WorkflowUiExperimentalApi import kotlin.reflect.KClass import kotlin.reflect.cast @@ -38,9 +38,9 @@ public object WorkflowAndroidXSupport { /** * Tries to get the parent [SavedStateRegistryOwner] from the current view via - * [ViewTreeSavedStateRegistryOwner], if that fails it looks up the context chain for a registry + * [findViewTreeSavedStateRegistryOwner], if that fails it looks up the context chain for a registry * owner, and if that fails it just returns null. This differs from - * [ViewTreeSavedStateRegistryOwner.get] because it will check the [View.getContext] if no owner + * [findViewTreeSavedStateRegistryOwner] because it will check the [View.getContext] if no owner * is found in the view tree. */ @WorkflowUiExperimentalApi @@ -51,14 +51,14 @@ public object WorkflowAndroidXSupport { /** * Tries to get the parent [SavedStateRegistryOwner] from the current view via - * [ViewTreeSavedStateRegistryOwner], if that fails it looks up the context chain for a registry + * [findViewTreeSavedStateRegistryOwner], if that fails it looks up the context chain for a registry * owner, and if that fails it just returns null. This differs from - * [ViewTreeSavedStateRegistryOwner.get] because it will check the [View.getContext] if no owner + * [findViewTreeSavedStateRegistryOwner] because it will check the [View.getContext] if no owner * is found in the view tree. */ @WorkflowUiExperimentalApi private fun stateRegistryOwnerFromViewTreeOrContextOrNull(view: View): SavedStateRegistryOwner? = - ViewTreeSavedStateRegistryOwner.get(view) + (view.findViewTreeSavedStateRegistryOwner()) ?: view.context.ownerOrNull(SavedStateRegistryOwner::class) private tailrec fun Context.ownerOrNull(ownerClass: KClass): T? = diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/WorkflowSavedStateRegistryAggregator.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/WorkflowSavedStateRegistryAggregator.kt index f92551319..429a4d2f5 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/WorkflowSavedStateRegistryAggregator.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/androidx/WorkflowSavedStateRegistryAggregator.kt @@ -9,11 +9,12 @@ import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewTreeLifecycleOwner import androidx.savedstate.SavedStateRegistryOwner -import androidx.savedstate.ViewTreeSavedStateRegistryOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.squareup.workflow1.ui.WorkflowUiExperimentalApi /** - * Manages a group of [ViewTreeSavedStateRegistryOwner]s that are all saved to + * Manages a group of [SavedStateRegistryOwner]s that are all saved to * and restored from a single "parent" [SavedStateRegistryOwner]. [SavedStateRegistryOwner] * is the new androidx alternative to the [View.onSaveInstanceState] system, and * is required by Compose UI. @@ -21,16 +22,16 @@ import com.squareup.workflow1.ui.WorkflowUiExperimentalApi * This class is designed to support a navigation container view that owns a * a set of navigation "frames", where a frame is something that can be navigated * to/from. A frame loosely consists of a root [View] and its - * [ViewTreeSavedStateRegistryOwner]. For example: + * [SavedStateRegistryOwner]. For example: * * - a back stack container view will own an instance of [WorkflowSavedStateRegistryAggregator], - * and use it to assign a [ViewTreeSavedStateRegistryOwner] for its top view. + * and use it to assign a [SavedStateRegistryOwner] for its top view. * * - a container view managing a set of windows will own an instance of - * [WorkflowSavedStateRegistryAggregator], and use it to assign a [ViewTreeSavedStateRegistryOwner] + * [WorkflowSavedStateRegistryAggregator], and use it to assign a [SavedStateRegistryOwner] * to each dialog's content view. * - * Note that a [ViewTreeSavedStateRegistryOwner] works _in parallel_ to a + * Note that a [SavedStateRegistryOwner] works _in parallel_ to a * [ViewTreeLifecycleOwner][androidx.lifecycle.ViewTreeLifecycleOwner]. * Use [WorkflowLifecycleOwner] to ensure one is properly installed. * @@ -38,7 +39,7 @@ import com.squareup.workflow1.ui.WorkflowUiExperimentalApi * and passed the parent registry. [detachFromParentRegistry] must be called when the * container view is detached. * - * Call [installChildRegistryOwnerOn] to put a [ViewTreeSavedStateRegistryOwner] + * Call [installChildRegistryOwnerOn] to put a [SavedStateRegistryOwner] * in place on each managed child view, _before it is attached to a window_. After that: * * - call [saveAndPruneChildRegistryOwner] if the child is removed from service but may @@ -180,7 +181,7 @@ public class WorkflowSavedStateRegistryAggregator { } /** - * Puts a new [ViewTreeSavedStateRegistryOwner] in place on [view], registered + * Puts a new [SavedStateRegistryOwner] in place on [view], registered * with its [ViewTreeLifecycleOwner]. (Use [WorkflowLifecycleOwner] to ensure * one is properly installed.) * @@ -200,7 +201,7 @@ public class WorkflowSavedStateRegistryAggregator { * back stack history is modified, call [prune] _with the keys of the views that * remain active_. * - * @param key identifier for the new [ViewTreeSavedStateRegistryOwner], unique across this + * @param key identifier for the new [SavedStateRegistryOwner], unique across this * [WorkflowSavedStateRegistryAggregator]. Typically this is derived from the * [compatibility key][com.squareup.workflow1.ui.Compatible.keyFor] of the [view]'s * rendering. @@ -217,10 +218,10 @@ public class WorkflowSavedStateRegistryAggregator { children.put(key, registryOwner)?.let { throw IllegalArgumentException("$key is already in use, it cannot be used to register $view") } - ViewTreeSavedStateRegistryOwner.get(view)?.let { - throw IllegalArgumentException("$view already has ViewTreeSavedStateRegistryOwner: $it") + view.findViewTreeSavedStateRegistryOwner()?.let { + throw IllegalArgumentException("$view already has SavedStateRegistryOwner: $it") } - ViewTreeSavedStateRegistryOwner.set(view, registryOwner) + view.setViewTreeSavedStateRegistryOwner(registryOwner) restoreIfOwnerReady(registryOwner) } @@ -261,7 +262,7 @@ public class WorkflowSavedStateRegistryAggregator { } /** - * Drops all child [ViewTreeSavedStateRegistryOwner]s and their restored + * Drops all child [SavedStateRegistryOwner]s and their restored * state, except those identified in [keysToKeep]. */ public fun pruneAllChildRegistryOwnersExcept(keysToKeep: Collection = emptyList()) { diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BackStackContainer.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BackStackContainer.kt index 72901c7fe..cc845c26f 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BackStackContainer.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BackStackContainer.kt @@ -1,6 +1,8 @@ package com.squareup.workflow1.ui.container import android.content.Context +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Parcel import android.os.Parcelable import android.os.Parcelable.Creator @@ -10,7 +12,6 @@ import android.view.View import android.view.animation.AccelerateDecelerateInterpolator import android.widget.FrameLayout import androidx.savedstate.SavedStateRegistry -import androidx.savedstate.ViewTreeSavedStateRegistryOwner import androidx.transition.Scene import androidx.transition.Slide import androidx.transition.TransitionManager @@ -39,7 +40,7 @@ import com.squareup.workflow1.ui.toViewFactory * This container supports saving and restoring the view state of each of its subviews corresponding * to the renderings in its [BackStackScreen]. It supports two distinct state mechanisms: * 1. Classic view hierarchy state ([View.onSaveInstanceState]/[View.onRestoreInstanceState]) - * 2. AndroidX [SavedStateRegistry] via [ViewTreeSavedStateRegistryOwner]. + * 2. AndroidX [SavedStateRegistry] via [SavedStateRegistryOwner]. */ @WorkflowUiExperimentalApi public open class BackStackContainer @JvmOverloads constructor( @@ -201,7 +202,15 @@ public open class BackStackContainer @JvmOverloads constructor( } public constructor(source: Parcel) : super(source) { - savedViewState = source.readParcelable(ViewStateCache.Saved::class.java.classLoader)!! + savedViewState = if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + source.readParcelable( + ViewStateCache.Saved::class.java.classLoader, + ViewStateCache.Saved::class.java + )!! + } else { + @Suppress("DEPRECATION") + source.readParcelable(ViewStateCache.Saved::class.java.classLoader)!! + } } public val savedViewState: ViewStateCache.Saved diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BodyAndOverlaysContainer.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BodyAndOverlaysContainer.kt index 5308f8464..a9e492939 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BodyAndOverlaysContainer.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/BodyAndOverlaysContainer.kt @@ -1,6 +1,8 @@ package com.squareup.workflow1.ui.container import android.content.Context +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Parcel import android.os.Parcelable import android.os.Parcelable.Creator @@ -116,8 +118,15 @@ internal class BodyAndOverlaysContainer @JvmOverloads constructor( } constructor(source: Parcel) : super(source) { - @Suppress("UNCHECKED_CAST") - savedDialogSessions = source.readParcelable(SavedState::class.java.classLoader)!! + savedDialogSessions = if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + source.readParcelable( + LayeredDialogSessions.SavedState::class.java.classLoader, + LayeredDialogSessions.SavedState::class.java + )!! + } else { + @Suppress("DEPRECATION") + source.readParcelable(LayeredDialogSessions.SavedState::class.java.classLoader)!! + } } val savedDialogSessions: LayeredDialogSessions.SavedState diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogSession.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogSession.kt index fef76299a..0eadc4e45 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogSession.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/DialogSession.kt @@ -129,7 +129,7 @@ internal class DialogSession( decorView, findParentLifecycle = { parentLifecycleOwner.lifecycle } ) - // Ensure that each dialog has its own ViewTreeSavedStateRegistryOwner, + // Ensure that each dialog has its own SavedStateRegistryOwner, // so views in each dialog layer don't clash with other layers. stateRegistryAggregator.installChildRegistryOwnerOn( view = decorView, diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/LayeredDialogSessions.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/LayeredDialogSessions.kt index 5bfe27cc6..c95b1f9d7 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/LayeredDialogSessions.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/LayeredDialogSessions.kt @@ -93,8 +93,8 @@ public class LayeredDialogSessions private constructor( private val getParentLifecycleOwner: () -> LifecycleOwner ) { /** - * Provides a new `ViewTreeSavedStateRegistryOwner` for each dialog, - * which will save to the `ViewTreeSavedStateRegistryOwner` of this container view. + * Provides a new `SavedStateRegistryOwner` for each dialog, + * which will save to the `SavedStateRegistryOwner` of this container view. */ private val stateRegistryAggregator = WorkflowSavedStateRegistryAggregator() diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/ViewStateCache.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/ViewStateCache.kt index b57129ebe..aee98e7ec 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/ViewStateCache.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/ViewStateCache.kt @@ -1,5 +1,7 @@ package com.squareup.workflow1.ui.container +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Parcel import android.os.Parcelable import android.os.Parcelable.Creator @@ -8,7 +10,6 @@ import android.view.View import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting.PRIVATE import androidx.savedstate.SavedStateRegistryOwner -import androidx.savedstate.ViewTreeSavedStateRegistryOwner import com.squareup.workflow1.ui.Compatible.Companion.keyFor import com.squareup.workflow1.ui.NamedScreen import com.squareup.workflow1.ui.ScreenViewHolder @@ -22,7 +23,7 @@ import com.squareup.workflow1.ui.showing * * - Provides [Parcelable]-based [save] and [restore] methods for use from a * container's [View.onSaveInstanceState] and [View.onRestoreInstanceState] methods. - * - Also handles androidx [ViewTreeSavedStateRegistryOwner] duties, via + * - Also handles androidx [SavedStateRegistryOwner] duties, via * a wrapped instance of [WorkflowSavedStateRegistryAggregator]. This means that container * views using this class must call [attachToParentRegistryOwner] and * [detachFromParentRegistry] when they are [attached][View.onAttachedToWindow] and @@ -85,7 +86,7 @@ internal constructor( } } - // Put the [ViewTreeSavedStateRegistryOwner] in place. + // Put the [SavedStateRegistryOwner] in place. stateRegistryAggregator.installChildRegistryOwnerOn(newHolder.view, newKey) viewStates.remove(newKey) @@ -154,10 +155,20 @@ internal constructor( this.viewStates = mutableMapOf() .apply { @Suppress("UNCHECKED_CAST") - source.readMap( - this as MutableMap, - ViewStateCache::class.java.classLoader - ) + if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + source.readMap( + this as MutableMap, + ViewStateCache::class.java.classLoader, + Any::class.java, + Any::class.java + ) + } else { + @Suppress("DEPRECATION") + source.readMap( + this as MutableMap, + ViewStateCache::class.java.classLoader + ) + } } .toMap() } diff --git a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/ViewStateFrame.kt b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/ViewStateFrame.kt index f25b0d84f..5447f134a 100644 --- a/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/ViewStateFrame.kt +++ b/workflow-ui/core-android/src/main/java/com/squareup/workflow1/ui/container/ViewStateFrame.kt @@ -1,5 +1,7 @@ package com.squareup.workflow1.ui.container +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import android.os.Parcel import android.os.Parcelable import android.os.Parcelable.Creator @@ -29,7 +31,15 @@ internal data class ViewStateFrame( companion object CREATOR : Creator { override fun createFromParcel(parcel: Parcel): ViewStateFrame { val key = parcel.readString()!! - val viewState = parcel.readSparseArray(ViewStateFrame::class.java.classLoader)!! + val viewState = if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + parcel.readSparseArray( + ViewStateFrame::class.java.classLoader, + Parcelable::class.java + )!! + } else { + @Suppress("DEPRECATION") + parcel.readSparseArray(ViewStateFrame::class.java.classLoader)!! + } return ViewStateFrame(key, viewState) } diff --git a/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/androidx/RealWorkflowLifecycleOwnerTest.kt b/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/androidx/RealWorkflowLifecycleOwnerTest.kt index 6425d6003..84d2c4c87 100644 --- a/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/androidx/RealWorkflowLifecycleOwnerTest.kt +++ b/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/androidx/RealWorkflowLifecycleOwnerTest.kt @@ -174,6 +174,8 @@ internal class RealWorkflowLifecycleOwnerTest { @Test fun `lifecycle stays in INITIALIZED when moved immediately to DESTROYED`() { val events = mutableListOf() ensureParentLifecycle() + // Cannot go directly to DESTROYED + parentLifecycle!!.currentState = CREATED parentLifecycle!!.currentState = DESTROYED // The lifecycle is more strict when there's at least one observer, so add one. owner.lifecycle.addObserver( diff --git a/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/androidx/WorkflowSavedStateRegistryAggregatorTest.kt b/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/androidx/WorkflowSavedStateRegistryAggregatorTest.kt index 1bf189875..068e570a8 100644 --- a/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/androidx/WorkflowSavedStateRegistryAggregatorTest.kt +++ b/workflow-ui/core-android/src/test/java/com/squareup/workflow1/ui/androidx/WorkflowSavedStateRegistryAggregatorTest.kt @@ -9,7 +9,8 @@ import androidx.lifecycle.LifecycleRegistry import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner -import androidx.savedstate.ViewTreeSavedStateRegistryOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner import androidx.test.core.app.ApplicationProvider import com.google.common.truth.Truth.assertThat import com.squareup.workflow1.ui.WorkflowUiExperimentalApi @@ -66,7 +67,7 @@ internal class WorkflowSavedStateRegistryAggregatorTest { @Test fun `install throws on redundant call`() { val view = View(ApplicationProvider.getApplicationContext()).apply { - ViewTreeSavedStateRegistryOwner.set(this, SimpleStateRegistry()) + this.setViewTreeSavedStateRegistryOwner(SimpleStateRegistry()) WorkflowLifecycleOwner.installOn(this) } @@ -78,7 +79,7 @@ internal class WorkflowSavedStateRegistryAggregatorTest { assertThat(error).hasMessageThat() .contains( - "already has ViewTreeSavedStateRegistryOwner: com.squareup.workflow1.ui.androidx." + + "already has SavedStateRegistryOwner: com.squareup.workflow1.ui.androidx." + "WorkflowSavedStateRegistryAggregatorTest\$SimpleStateRegistry" ) } @@ -310,10 +311,10 @@ internal class WorkflowSavedStateRegistryAggregatorTest { private class SimpleStateRegistry : SavedStateRegistryOwner { val lifecycleRegistry = LifecycleRegistry(this) val stateRegistryController = SavedStateRegistryController.create(this) + override val savedStateRegistry: SavedStateRegistry + get() = stateRegistryController.savedStateRegistry override fun getLifecycle(): Lifecycle = lifecycleRegistry - override fun getSavedStateRegistry(): SavedStateRegistry = - stateRegistryController.savedStateRegistry fun saveToBundle(): Bundle = Bundle().also { bundle -> stateRegistryController.performSave(bundle) @@ -321,5 +322,5 @@ internal class WorkflowSavedStateRegistryAggregatorTest { } private val View.savedStateRegistry: SavedStateRegistry - get() = ViewTreeSavedStateRegistryOwner.get(this)!!.savedStateRegistry + get() = this.findViewTreeSavedStateRegistryOwner()!!.savedStateRegistry } diff --git a/workflow-ui/internal-testing-android/api/internal-testing-android.api b/workflow-ui/internal-testing-android/api/internal-testing-android.api index e450ec62a..68a9699a0 100644 --- a/workflow-ui/internal-testing-android/api/internal-testing-android.api +++ b/workflow-ui/internal-testing-android/api/internal-testing-android.api @@ -47,13 +47,6 @@ public final class com/squareup/workflow1/ui/internal/test/AbstractLifecycleTest public static fun onViewTreeLifecycleStateChanged (Lcom/squareup/workflow1/ui/internal/test/AbstractLifecycleTestActivity$ViewObserver;Ljava/lang/Object;Landroidx/lifecycle/Lifecycle$Event;)V } -public final class com/squareup/workflow1/ui/internal/test/DetectLeaksAfterTestSuccess : org/junit/rules/TestRule { - public fun ()V - public fun (Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun apply (Lorg/junit/runners/model/Statement;Lorg/junit/runner/Description;)Lorg/junit/runners/model/Statement; -} - public final class com/squareup/workflow1/ui/internal/test/EspressoKt { public static final field DEFAULT_RETRY_TIMEOUT J public static final field RETRY_POLLING_INTERVAL J diff --git a/workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/DetectLeaksAfterTestSuccess.kt b/workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/DetectLeaksAfterTestSuccess.kt deleted file mode 100644 index 7a2dae6ff..000000000 --- a/workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/DetectLeaksAfterTestSuccess.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.squareup.workflow1.ui.internal.test - -import leakcanary.AppWatcher -import leakcanary.LeakAssertions -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement - -/** - * Forked from Leakcanary until https://github.com/square/leakcanary/issues/2297 is fixed. - * - * [TestRule] that invokes [LeakAssertions.assertNoLeaks] after the test - * successfully evaluates. Pay attention to where you set up this rule in the - * rule chain as you might detect different leaks (e.g. around vs wrapped by the - * activity rule). It's also possible to use this rule several times in a rule - * chain. - */ -public class DetectLeaksAfterTestSuccess( - private val tag: String = DetectLeaksAfterTestSuccess::class.java.simpleName -) : TestRule { - override fun apply( - base: Statement, - description: Description - ): Statement { - return object : Statement() { - override fun evaluate() { - // If the test fails, evaluate() will throw and we won't run the analysis (which is good). - try { - base.evaluate() - LeakAssertions.assertNoLeaks(tag) - } finally { - // Otherwise upstream test failures will be reported as leaks. - // https://github.com/square/leakcanary/issues/2297 - AppWatcher.objectWatcher.clearWatchedObjects() - } - } - } - } -} diff --git a/workflow-ui/radiography/dependencies/releaseRuntimeClasspath.txt b/workflow-ui/radiography/dependencies/releaseRuntimeClasspath.txt index ea1a7c64a..93c44d16f 100644 --- a/workflow-ui/radiography/dependencies/releaseRuntimeClasspath.txt +++ b/workflow-ui/radiography/dependencies/releaseRuntimeClasspath.txt @@ -2,24 +2,26 @@ :workflow-runtime :workflow-ui:core-android :workflow-ui:core-common -androidx.activity:activity:1.3.0 +androidx.activity:activity:1.6.1 androidx.annotation:annotation-experimental:1.1.0 androidx.annotation:annotation:1.2.0 androidx.arch.core:core-common:2.1.0 androidx.arch.core:core-runtime:2.1.0 androidx.collection:collection:1.1.0 +androidx.concurrent:concurrent-futures:1.0.0 androidx.core:core-ktx:1.6.0 -androidx.core:core:1.6.0 -androidx.lifecycle:lifecycle-common:2.4.0 -androidx.lifecycle:lifecycle-livedata-core:2.4.0 -androidx.lifecycle:lifecycle-runtime-ktx:2.4.0 -androidx.lifecycle:lifecycle-runtime:2.4.0 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0 -androidx.lifecycle:lifecycle-viewmodel:2.4.0 -androidx.savedstate:savedstate:1.1.0 +androidx.core:core:1.8.0 +androidx.lifecycle:lifecycle-common:2.5.1 +androidx.lifecycle:lifecycle-livedata-core:2.5.1 +androidx.lifecycle:lifecycle-runtime-ktx:2.5.1 +androidx.lifecycle:lifecycle-runtime:2.5.1 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1 +androidx.lifecycle:lifecycle-viewmodel:2.5.1 +androidx.savedstate:savedstate:1.2.0 androidx.tracing:tracing:1.0.0 androidx.transition:transition:1.4.1 androidx.versionedparcelable:versionedparcelable:1.1.1 +com.google.guava:listenablefuture:1.0 com.squareup.curtains:curtains:1.2.2 com.squareup.okio:okio-jvm:3.0.0 com.squareup.okio:okio:3.0.0