From 9381294b929c6c01365f0ae12c855510a739729e Mon Sep 17 00:00:00 2001 From: Stephen Edwards Date: Wed, 22 Feb 2023 18:01:54 -0500 Subject: [PATCH] compileSdkVersion 33; androidx.activity/appcompat upgrades; transitives Fix RenderAsState which was exposed by test as state was not set initially always. --- benchmarks/dungeon-benchmark/build.gradle.kts | 7 +- .../complex-benchmark/build.gradle.kts | 7 +- .../complex-poetry/build.gradle.kts | 7 +- buildSrc/src/main/java/Versions.kt | 4 +- .../src/main/java/android-defaults.gradle.kts | 8 +- .../main/java/android-sample-app.gradle.kts | 7 + .../src/main/java/android-ui-tests.gradle.kts | 1 - gradle/libs.versions.toml | 48 ++--- samples/compose-samples/build.gradle.kts | 4 + samples/compose-samples/lint-baseline.xml | 33 ++++ .../compose/hellocompose/HelloComposeTest.kt | 2 +- .../hellocomposebinding/HelloBindingTest.kt | 2 +- .../HelloComposeWorkflowTest.kt | 2 +- .../inlinerendering/InlineRenderingTest.kt | 2 +- .../compose/launcher/SampleLauncherTest.kt | 4 +- .../nestedrenderings/NestedRenderingsTest.kt | 2 +- .../sample/compose/preview/PreviewTest.kt | 2 +- .../sample/compose/textinput/TextInputTest.kt | 2 +- .../src/main/AndroidManifest.xml | 3 +- .../InlineRenderingWorkflow.kt | 9 +- .../compose/launcher/SampleLauncherApp.kt | 13 +- samples/containers/android/build.gradle.kts | 3 + .../containers/app-poetry/lint-baseline.xml | 15 ++ .../sample/poetryapp/PoetryAppTest.kt | 2 +- .../app-poetry/src/main/AndroidManifest.xml | 3 +- .../containers/app-raven/lint-baseline.xml | 15 ++ .../squareup/sample/ravenapp/RavenAppTest.kt | 2 +- .../app-raven/src/main/AndroidManifest.xml | 3 +- .../hello-back-button/lint-baseline.xml | 26 +++ .../HelloBackButtonEspressoTest.kt | 2 +- .../src/main/AndroidManifest.xml | 3 +- samples/dungeon/app/lint-baseline.xml | 81 ++++++++ .../squareup/sample/dungeon/DungeonAppTest.kt | 2 +- .../dungeon/app/src/main/AndroidManifest.xml | 3 +- .../timemachine/shakeable/ShakeWorker.kt | 2 +- .../hello-workflow-fragment/lint-baseline.xml | 59 ++++++ .../HelloWorkflowFragmentAppTest.kt | 2 +- .../src/main/AndroidManifest.xml | 3 +- samples/hello-workflow/lint-baseline.xml | 15 ++ .../helloworkflow/HelloWorkflowAppTest.kt | 2 +- .../src/main/AndroidManifest.xml | 3 +- samples/stub-visibility/lint-baseline.xml | 26 +++ .../stubvisibility/StubVisibilityAppTest.kt | 2 +- .../src/main/AndroidManifest.xml | 3 +- samples/tictactoe/app/lint-baseline.xml | 180 ++++++++++++++++++ .../squareup/sample/TicTacToeEspressoTest.kt | 2 +- .../app/src/main/AndroidManifest.xml | 3 +- samples/todo-android/app/lint-baseline.xml | 70 +++++++ .../sample/mainactivity/TodoAppTest.kt | 2 +- .../app/src/main/AndroidManifest.xml | 3 +- .../tutorial/tutorial-1-complete/build.gradle | 4 +- .../tutorial/tutorial-2-complete/build.gradle | 4 +- .../tutorial/tutorial-3-complete/build.gradle | 4 +- .../tutorial/tutorial-4-complete/build.gradle | 4 +- samples/tutorial/tutorial-base/build.gradle | 4 +- samples/tutorial/tutorial-final/build.gradle | 4 +- samples/tutorial/tutorial-views/build.gradle | 4 +- workflow-ui/compose-tooling/build.gradle.kts | 4 + .../dependencies/releaseRuntimeClasspath.txt | 59 +++--- .../tooling/LegacyPreviewViewFactoryTest.kt | 2 +- .../compose/tooling/PreviewViewFactoryTest.kt | 2 +- workflow-ui/compose/README.md | 8 +- workflow-ui/compose/build.gradle.kts | 4 + .../dependencies/releaseRuntimeClasspath.txt | 52 ++--- .../ui/compose/ComposeViewFactoryTest.kt | 2 +- .../compose/ComposeViewTreeIntegrationTest.kt | 2 +- .../ui/compose/CompositionRootTest.kt | 2 +- .../compose/LegacyComposeViewFactoryTest.kt | 2 +- .../LegacyComposeViewTreeIntegrationTest.kt | 2 +- .../ui/compose/LegacyWorkflowRenderingTest.kt | 5 +- .../workflow1/ui/compose/RenderAsStateTest.kt | 2 +- .../ui/compose/WorkflowRenderingTest.kt | 4 +- .../workflow1/ui/compose/CompositionRoot.kt | 2 +- .../workflow1/ui/compose/RenderAsState.kt | 23 ++- .../dependencies/releaseRuntimeClasspath.txt | 37 ++-- .../test/ModalViewContainerLifecycleTest.kt | 2 +- .../workflow1/ui/modal/ModalContainer.kt | 8 +- workflow-ui/core-android/build.gradle.kts | 1 + .../dependencies/releaseRuntimeClasspath.txt | 20 +- .../workflow1/ui/BackPressedHandlerTest.kt | 2 +- .../ui/WorkflowViewStubLifecycleTest.kt | 17 +- ...kStackContainerPersistenceLifecycleTest.kt | 2 +- .../ui/container/BackStackContainerTest.kt | 12 +- .../ui/container/ViewStateCacheTest.kt | 16 +- .../squareup/workflow1/ui/SnapshotParcels.kt | 9 +- .../workflow1/ui/TreeSnapshotSaver.kt | 18 +- .../squareup/workflow1/ui/WorkflowLayout.kt | 13 +- .../squareup/workflow1/ui/WorkflowViewStub.kt | 19 +- .../androidx/KeyedSavedStateRegistryOwner.kt | 9 +- .../ui/androidx/WorkflowAndroidXSupport.kt | 12 +- .../WorkflowSavedStateRegistryAggregator.kt | 27 +-- .../ui/container/BackStackContainer.kt | 15 +- .../ui/container/BodyAndOverlaysContainer.kt | 13 +- .../workflow1/ui/container/DialogSession.kt | 2 +- .../ui/container/LayeredDialogSessions.kt | 4 +- .../workflow1/ui/container/ViewStateCache.kt | 25 ++- .../workflow1/ui/container/ViewStateFrame.kt | 12 +- .../RealWorkflowLifecycleOwnerTest.kt | 2 + ...orkflowSavedStateRegistryAggregatorTest.kt | 13 +- .../api/internal-testing-android.api | 7 - .../test/DetectLeaksAfterTestSuccess.kt | 39 ---- .../dependencies/releaseRuntimeClasspath.txt | 20 +- 102 files changed, 968 insertions(+), 319 deletions(-) create mode 100644 samples/compose-samples/lint-baseline.xml create mode 100644 samples/containers/app-poetry/lint-baseline.xml create mode 100644 samples/containers/app-raven/lint-baseline.xml create mode 100644 samples/containers/hello-back-button/lint-baseline.xml create mode 100644 samples/dungeon/app/lint-baseline.xml create mode 100644 samples/hello-workflow-fragment/lint-baseline.xml create mode 100644 samples/hello-workflow/lint-baseline.xml create mode 100644 samples/stub-visibility/lint-baseline.xml create mode 100644 samples/tictactoe/app/lint-baseline.xml create mode 100644 samples/todo-android/app/lint-baseline.xml delete mode 100644 workflow-ui/internal-testing-android/src/main/java/com/squareup/workflow1/ui/internal/test/DetectLeaksAfterTestSuccess.kt diff --git a/benchmarks/dungeon-benchmark/build.gradle.kts b/benchmarks/dungeon-benchmark/build.gradle.kts index 6f251f2179..0d9c6e2387 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 ee759e3fde..239d5dd7a7 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 62f9d8e00e..be00729913 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 e23baf8bb4..6f2234c4d3 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 33cc37a08e..aec33b29f9 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 ac3a62524a..34418c28c3 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 5e9a5e37c0..542a33f268 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 105d525966..4019c2d339 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 f7b69d2365..884670c89b 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 0000000000..48c45f24ad --- /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 1cccd88955..f90aea904e 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 d6f4ba3f39..a79a295d92 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 bb2eb58fe5..fb9e131c1f 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 a38736b477..b6e3566ed2 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 4a8fd50327..cd0a22f3c1 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 504549c456..3a1376f94b 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 5e538a6fe8..ec222c5fdb 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 a77e60c0b9..adfc481f8a 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 2d3dc35ba5..4df8623c76 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 f02aa0e629..8e53b70265 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 105d121eaa..51ccdd4039 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 0000000000..ed333fbf83 --- /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 775ccc044a..6bc56540d7 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 8b398d0b2d..26594ffd1b 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 0000000000..ed333fbf83 --- /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 753c6186e5..b1644b1e95 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 e5f581d7c3..17c7cdd283 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 0000000000..a1c902b19e --- /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 3971552b94..c353358a50 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 4789f9e258..a682370fd9 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 0000000000..37e236431e --- /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 2bf2c51b5b..c38a2e8e02 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 a8cc3bb22d..99776c041c 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 79b69b0477..7d71869ccb 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 0000000000..5d55093fb7 --- /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 73640814a8..78ad2d60ac 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 38b8dc545d..7571e4d6bc 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 0000000000..ed333fbf83 --- /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 b9045d4b89..66dbdb8559 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 e3614bb156..131b4bc730 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 0000000000..2ee1c7d31a --- /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 eac0b73145..a5f216963a 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 ab0c63d5e8..0fc6135870 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 0000000000..4aa688a7e2 --- /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 563b7a8768..12bd4c2f73 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 24d6db423f..caafc96bf7 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 0000000000..8d0983b2f0 --- /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 1ee097ff8b..4f88c66763 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 73f695f625..e817b0579b 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 c9c7e8b7b8..973fa05453 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 c9b9f9d056..0e020e74f3 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 c9b9f9d056..0e020e74f3 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 c9b9f9d056..0e020e74f3 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 4ff9af2363..a58e90c29c 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 8b8cb1e6fe..c18d1f2e78 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 7e94a61699..d680733b4c 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 bf7e67d629..dbaca35e1f 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 4b28918d45..1cf9fe4009 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 4d43c90609..6338b6c947 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 2af1f0c582..bba768ca6a 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 558c1db31f..4f2c8b6b28 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 7d37d9de0a..b880961f0b 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 7b6d478358..446563ec1e 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 af9f0d1814..6f1aae60ab 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 ce8d343def..5f77119634 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 abd78f668b..bce16d6a7d 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 1daec571b9..8ee54914f8 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 f9f4768891..3120134bd6 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 a465216e1e..89e5a36f1f 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 2b29f540c2..e060be47e5 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 acd205fd8d..a858d5b7b5 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 dcd725d183..35e3f54baa 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 7bc5e41b12..0e5d53732f 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 808d6b0c0e..f15e3dad90 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 25f3aa6bf5..6b871edeb2 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 8e814b36ca..a28b2c572e 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 f1b403931e..5b16b23084 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 c140250a83..b348ba0ef7 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 c40530dc10..62ec7f9623 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 a9bfffa7fd..c8a842bb67 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 de2cd2649e..60d69c4a22 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 ea7d2e914e..ee0192873e 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 99818f3ed6..f224b35f44 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 22eb38e8b1..4ee8c37f7c 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 12c5a72076..4c78a97073 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 ab25c0338d..388194e929 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 0253ae2556..4993a6b7fa 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 47953e94a8..dc2bb3346d 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 a0c25d73b9..347b2af8d1 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 f92551319b..429a4d2f51 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 72901c7fe7..cc845c26f1 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 5308f8464c..a9e4929394 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 fef76299ae..0eadc4e45f 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 5bfe27cc69..c95b1f9d7e 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 b57129ebeb..aee98e7ec7 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 f25b0d84f8..5447f134ae 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 6425d60036..84d2c4c87a 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 1bf1898758..068e570a88 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 e450ec62ad..68a9699a02 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 7a2dae6ff8..0000000000 --- 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 ea1a7c64a6..93c44d16f8 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