From 03fb18ba31d5537eea384ff3446d400acc1dcd9f Mon Sep 17 00:00:00 2001 From: Sean Proctor Date: Tue, 31 Jan 2023 13:25:24 -0500 Subject: [PATCH 01/89] add experimental js support to compose-jb extension --- extensions-compose-jetbrains/build.gradle.kts | 3 +++ extensions-compose-jetbrains/gradle.properties | 1 + 2 files changed, 4 insertions(+) create mode 100644 extensions-compose-jetbrains/gradle.properties diff --git a/extensions-compose-jetbrains/build.gradle.kts b/extensions-compose-jetbrains/build.gradle.kts index 7dc124b48..8a1f7de9f 100644 --- a/extensions-compose-jetbrains/build.gradle.kts +++ b/extensions-compose-jetbrains/build.gradle.kts @@ -14,6 +14,9 @@ plugins { setupMultiplatform { android() jvm() + js(IR) { + browser() + } } setupPublication() diff --git a/extensions-compose-jetbrains/gradle.properties b/extensions-compose-jetbrains/gradle.properties new file mode 100644 index 000000000..9c8f6b37b --- /dev/null +++ b/extensions-compose-jetbrains/gradle.properties @@ -0,0 +1 @@ +org.jetbrains.compose.experimental.jscanvas.enabled=true From 8933832fa5e37e3b7ada61a7b04a567537f4fdc7 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Wed, 1 Feb 2023 19:15:48 +0000 Subject: [PATCH 02/89] Removed extensions-compose-jetpack module --- .github/workflows/build.yml | 7 - deps.versions.toml | 4 +- .../api/extensions-compose-jetpack.api | 75 -------- extensions-compose-jetpack/build.gradle.kts | 45 ----- .../compose/jetpack/stack/ChildrenTest.kt | 174 ------------------ .../src/main/AndroidManifest.xml | 2 - .../compose/jetpack/SubscribeAsState.kt | 25 --- .../compose/jetpack/stack/Children.kt | 79 -------- .../stack/animation/DefaultStackAnimation.kt | 163 ---------------- .../stack/animation/DefaultStackAnimator.kt | 42 ----- .../jetpack/stack/animation/Direction.kt | 53 ------ .../stack/animation/EmptyStackAnimation.kt | 10 - .../compose/jetpack/stack/animation/Fade.kt | 22 --- .../compose/jetpack/stack/animation/Scale.kt | 27 --- .../compose/jetpack/stack/animation/Slide.kt | 23 --- .../jetpack/stack/animation/StackAnimation.kt | 46 ----- .../jetpack/stack/animation/StackAnimator.kt | 74 -------- settings.gradle.kts | 1 - tools/check-publication/build.gradle.kts | 1 - 19 files changed, 1 insertion(+), 872 deletions(-) delete mode 100644 extensions-compose-jetpack/api/extensions-compose-jetpack.api delete mode 100644 extensions-compose-jetpack/build.gradle.kts delete mode 100644 extensions-compose-jetpack/src/androidTest/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/ChildrenTest.kt delete mode 100644 extensions-compose-jetpack/src/main/AndroidManifest.xml delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/SubscribeAsState.kt delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/Children.kt delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/DefaultStackAnimation.kt delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/DefaultStackAnimator.kt delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction.kt delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/EmptyStackAnimation.kt delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Fade.kt delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Scale.kt delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Slide.kt delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation.kt delete mode 100644 extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64aca6035..d97d4c566 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,13 +26,6 @@ jobs: uses: gradle/gradle-build-action@v2 with: arguments: build -Dsplit_targets - - name: Android instrumentation tests - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: 21 - arch: x86 - disable-animations: true - script: ./gradlew :extensions-compose-jetpack:connectedDebugAndroidTest --stacktrace macos-build: name: Build on macOS runs-on: macos-11 diff --git a/deps.versions.toml b/deps.versions.toml index 17f649e6d..6a5835a7a 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -46,9 +46,7 @@ jetbrains-kotlinWrappers-kotlinWrappersBom = { group = "org.jetbrains.kotlin-wra jetbrains-kotlinx-binaryCompatibilityValidator = { group = "org.jetbrains.kotlinx", name = "binary-compatibility-validator", version.ref = "jetbrainsBinaryCompatibilityValidator" } jetbrains-kotlinx-kotlinxCoroutinesSwing = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "jetbrainsKotlinxCoroutines" } -androidx-compose-foundation-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "jetpackCompose" } -androidx-compose-ui-uiTestJunit4 = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "jetpackCompose" } -androidx-compose-ui-uiTestManifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "jetpackCompose" } +androidx-compose-runtime-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "jetpackCompose" } android-gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradle" } android-material-material = { group = "com.google.android.material", name = "material", version.ref = "androidMaterial" } diff --git a/extensions-compose-jetpack/api/extensions-compose-jetpack.api b/extensions-compose-jetpack/api/extensions-compose-jetpack.api deleted file mode 100644 index 289cff63f..000000000 --- a/extensions-compose-jetpack/api/extensions-compose-jetpack.api +++ /dev/null @@ -1,75 +0,0 @@ -public final class com/arkivanov/decompose/extensions/compose/jetpack/BuildConfig { - public static final field BUILD_TYPE Ljava/lang/String; - public static final field DEBUG Z - public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; - public fun ()V -} - -public final class com/arkivanov/decompose/extensions/compose/jetpack/SubscribeAsStateKt { - public static final fun subscribeAsState (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/runtime/Composer;I)Landroidx/compose/runtime/State; -} - -public final class com/arkivanov/decompose/extensions/compose/jetpack/stack/ChildrenKt { - public static final fun Children (Lcom/arkivanov/decompose/router/stack/ChildStack;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V - public static final fun Children (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/ComposableSingletons$SlideKt { - public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/ComposableSingletons$SlideKt; - public static field lambda-1 Lkotlin/jvm/functions/Function5; - public fun ()V - public final fun getLambda-1$extensions_compose_jetpack_release ()Lkotlin/jvm/functions/Function5; -} - -public final class com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction : java/lang/Enum { - public static final field ENTER_BACK Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction; - public static final field ENTER_FRONT Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction; - public static final field EXIT_BACK Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction; - public static final field EXIT_FRONT Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction; - public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction; - public static fun values ()[Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction; -} - -public final class com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/DirectionKt { - public static final fun isBack (Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction;)Z - public static final fun isEnter (Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction;)Z - public static final fun isExit (Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction;)Z - public static final fun isFront (Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction;)Z -} - -public final class com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/FadeKt { - public static final fun fade (Landroidx/compose/animation/core/FiniteAnimationSpec;F)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator; - public static synthetic fun fade$default (Landroidx/compose/animation/core/FiniteAnimationSpec;FILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator; -} - -public final class com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/ScaleKt { - public static final fun scale (Landroidx/compose/animation/core/FiniteAnimationSpec;FF)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator; - public static synthetic fun scale$default (Landroidx/compose/animation/core/FiniteAnimationSpec;FFILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator; -} - -public final class com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/SlideKt { - public static final fun slide (Landroidx/compose/animation/core/FiniteAnimationSpec;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator; - public static synthetic fun slide$default (Landroidx/compose/animation/core/FiniteAnimationSpec;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator; -} - -public abstract interface class com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation { - public abstract fun invoke (Lcom/arkivanov/decompose/router/stack/ChildStack;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimationKt { - public static final fun stackAnimation (Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator;Z)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation; - public static final fun stackAnimation (ZLkotlin/jvm/functions/Function3;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation; - public static synthetic fun stackAnimation$default (Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator;ZILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation; - public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation; -} - -public abstract interface class com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator { - public abstract fun invoke (Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimatorKt { - public static final fun plus (Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator;Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator; - public static final fun stackAnimator (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator; - public static synthetic fun stackAnimator$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator; -} - diff --git a/extensions-compose-jetpack/build.gradle.kts b/extensions-compose-jetpack/build.gradle.kts deleted file mode 100644 index 692c64f62..000000000 --- a/extensions-compose-jetpack/build.gradle.kts +++ /dev/null @@ -1,45 +0,0 @@ -import com.arkivanov.gradle.setupAndroidLibrary -import com.arkivanov.gradle.setupBinaryCompatibilityValidator -import com.arkivanov.gradle.setupPublication - -plugins { - id("com.android.library") - id("kotlin-android") - id("com.arkivanov.gradle.setup") -} - -setupAndroidLibrary() -setupPublication() -setupBinaryCompatibilityValidator() - -android { - namespace = "com.arkivanov.decompose.extensions.compose.jetpack" - - buildFeatures { - compose = true - } - - composeOptions { - kotlinCompilerExtensionVersion = deps.versions.jetpackComposeCompiler.get() - } - - defaultConfig { - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - packagingOptions { - pickFirst("META-INF/AL2.0") - pickFirst("META-INF/LGPL2.0") - pickFirst("META-INF/LGPL2.1") - } -} - -dependencies { - implementation(project(":decompose")) - implementation(deps.androidx.compose.foundation.foundation) - implementation(deps.androidx.activity.activityKtx) - androidTestImplementation(deps.androidx.compose.ui.uiTestJunit4) - androidTestImplementation(deps.junit.junit) - androidTestImplementation(deps.androidx.compose.ui.uiTestManifest) - androidTestImplementation(deps.androidx.test.core) -} diff --git a/extensions-compose-jetpack/src/androidTest/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/ChildrenTest.kt b/extensions-compose-jetpack/src/androidTest/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/ChildrenTest.kt deleted file mode 100644 index 621685f5d..000000000 --- a/extensions-compose-jetpack/src/androidTest/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/ChildrenTest.kt +++ /dev/null @@ -1,174 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.State -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.performClick -import com.arkivanov.decompose.Child -import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.StackAnimation -import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.fade -import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.plus -import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.scale -import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.slide -import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.stackAnimation -import com.arkivanov.decompose.router.stack.ChildStack -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import java.io.Serializable - -@Suppress("TestFunctionName") -@RunWith(Parameterized::class) -class ChildrenTest( - private val animation: StackAnimation, -) { - - @get:Rule - val composeRule = createComposeRule() - - @Test - fun WHEN_active_child_and_no_back_stack_THEN_active_child_displayed() { - val state = mutableStateOf(routerState(activeConfig = Config.A)) - - setContent(state) - - composeRule.onNodeWithText(text = "ChildA", substring = true).assertExists() - } - - @Test - fun GIVEN_child_A_displayed_WHEN_push_child_B_THEN_child_B_displayed() { - val state = mutableStateOf(routerState(activeConfig = Config.A)) - setContent(state) - - state.setValueOnIdle(routerState(activeConfig = Config.B, backstack = listOf(Config.A))) - - composeRule.onNodeWithText(text = "ChildB", substring = true).assertExists() - } - - @Test - fun GIVEN_child_B_displayed_and_child_A_in_back_stack_WHEN_pop_child_B_THEN_child_A_displayed() { - val state = mutableStateOf(routerState(activeConfig = Config.A)) - setContent(state) - state.setValueOnIdle(routerState(activeConfig = Config.B, backstack = listOf(Config.A))) - - state.setValueOnIdle(routerState(activeConfig = Config.A)) - - composeRule.onNodeWithText(text = "ChildA", substring = true).assertExists() - } - - @Test - fun GIVEN_child_B_displayed_and_child_A_in_back_stack_WHEN_pop_child_B_THEN_state_restored_for_child_A() { - val state = mutableStateOf(routerState(activeConfig = Config.A)) - setContent(state) - composeRule.onNodeWithText(text = "ChildA=0").performClick() - state.setValueOnIdle(routerState(activeConfig = Config.B, backstack = listOf(Config.A))) - - state.setValueOnIdle(routerState(activeConfig = Config.A)) - - composeRule.onNodeWithText(text = "ChildA=1").assertExists() - } - - @Test - fun GIVEN_child_B_displayed_and_child_A_in_back_stack_WHEN_pop_child_B_and_push_child_B_THEN_state_not_restored_for_child_B() { - val state = mutableStateOf(routerState(activeConfig = Config.A)) - setContent(state) - state.setValueOnIdle(routerState(activeConfig = Config.B, backstack = listOf(Config.A))) - composeRule.onNodeWithText(text = "ChildB=0").performClick() - - state.setValueOnIdle(routerState(activeConfig = Config.A)) - state.setValueOnIdle(routerState(activeConfig = Config.B, backstack = listOf(Config.A))) - - composeRule.onNodeWithText(text = "ChildB=0").assertExists() - } - - @Test - fun GIVEN_child_A_displayed_WHEN_push_child_B_THEN_child_A_disposed() { - val state = mutableStateOf(routerState(activeConfig = Config.A)) - setContent(state) - - state.setValueOnIdle(routerState(activeConfig = Config.B, backstack = listOf(Config.A))) - - composeRule.onNodeWithText(text = "ChildA", substring = true).assertDoesNotExist() - } - - @Test - fun GIVEN_child_B_displayed_and_child_A_in_back_stack_WHEN_pop_child_B_THEN_child_B_disposed() { - val state = mutableStateOf(routerState(activeConfig = Config.A)) - setContent(state) - state.setValueOnIdle(routerState(activeConfig = Config.B, backstack = listOf(Config.A))) - - state.setValueOnIdle(routerState(activeConfig = Config.A)) - - composeRule.onNodeWithText(text = "ChildB", substring = true).assertDoesNotExist() - } - - private fun setContent(stack: State>) { - composeRule.setContent { - Children(stack = stack.value, animation = animation) { child -> - when (child.configuration) { - Config.A -> Child(name = "A") - Config.B -> Child(name = "B") - }.let {} - } - } - - runOnIdle {} - } - - private fun routerState(activeConfig: Config, backstack: List = emptyList()): ChildStack = - ChildStack( - active = Child.Created(configuration = activeConfig, instance = activeConfig), - backStack = backstack.map { Child.Destroyed(configuration = it) } - ) - - @Composable - private fun Child(name: String) { - var count by rememberSaveable { mutableStateOf(0) } - - BasicText( - text = "Child$name=$count", - modifier = Modifier.clickable { count++ } - ) - } - - private fun MutableState.setValueOnIdle(value: T) { - runOnIdle { this.value = value } - runOnIdle {} - } - - private fun runOnIdle(block: () -> Unit) { - composeRule.runOnIdle(block) - } - - companion object { - @Parameterized.Parameters - @JvmStatic - fun parameters(): List> = - getParameters().map { arrayOf(it) } - - private fun getParameters(): List> = - listOf( - stackAnimation { _, _, _ -> null }, - stackAnimation(scale()), - stackAnimation(fade()), - stackAnimation(slide()), - stackAnimation(scale() + fade() + slide()), - ) - } - - // Can be enum, workaround https://issuetracker.google.com/issues/195185633 - sealed class Config : Serializable { - object A : Config() - object B : Config() - } -} diff --git a/extensions-compose-jetpack/src/main/AndroidManifest.xml b/extensions-compose-jetpack/src/main/AndroidManifest.xml deleted file mode 100644 index 5c3d3655b..000000000 --- a/extensions-compose-jetpack/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/SubscribeAsState.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/SubscribeAsState.kt deleted file mode 100644 index 61f2c61d8..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/SubscribeAsState.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import com.arkivanov.decompose.value.Value - -@Composable -fun Value.subscribeAsState(): State { - val state = remember(this) { mutableStateOf(value) } - - DisposableEffect(this) { - val observer: (T) -> Unit = { state.value = it } - - subscribe(observer) - - onDispose { - unsubscribe(observer) - } - } - - return state -} diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/Children.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/Children.kt deleted file mode 100644 index 4406a414b..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/Children.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack - -import android.annotation.SuppressLint -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.SaveableStateHolder -import androidx.compose.runtime.saveable.rememberSaveableStateHolder -import androidx.compose.ui.Modifier -import com.arkivanov.decompose.Child -import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.StackAnimation -import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.emptyStackAnimation -import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState -import com.arkivanov.decompose.router.stack.ChildStack -import com.arkivanov.decompose.value.Value - -@Composable -fun Children( - stack: ChildStack, - modifier: Modifier = Modifier, - animation: StackAnimation? = null, - content: @Composable (child: Child.Created) -> Unit, -) { - val holder = rememberSaveableStateHolder() - - holder.retainStates(stack.getConfigurations()) - - val anim = animation ?: emptyStackAnimation() - - anim(stack = stack, modifier = modifier) { child -> - holder.SaveableStateProvider(child.configuration.key()) { - content(child) - } - } -} - -@Composable -fun Children( - stack: Value>, - modifier: Modifier = Modifier, - animation: StackAnimation? = null, - content: @Composable (child: Child.Created) -> Unit, -) { - val state = stack.subscribeAsState() - - Children( - stack = state.value, - modifier = modifier, - animation = animation, - content = content - ) -} - -private fun ChildStack<*, *>.getConfigurations(): Set = - items.mapTo(HashSet()) { it.configuration.key() } - -private fun Any.key(): String = "${this::class.simpleName}_${hashCode().toString(radix = 36)}" - -@SuppressLint("ComposableNaming") -@Composable -private fun SaveableStateHolder.retainStates(currentKeys: Set) { - val keys = remember(this) { Keys(currentKeys) } - - DisposableEffect(this, currentKeys) { - keys.set.forEach { - if (it !in currentKeys) { - removeState(it) - } - } - - keys.set = currentKeys - - onDispose {} - } -} - -private class Keys( - var set: Set -) diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/DefaultStackAnimation.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/DefaultStackAnimation.kt deleted file mode 100644 index 09f57287e..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/DefaultStackAnimation.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack.animation - -import androidx.compose.foundation.layout.Box -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.key -import androidx.compose.runtime.movableContentOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.pointer.consumeAllChanges -import androidx.compose.ui.input.pointer.pointerInput -import com.arkivanov.decompose.Child -import com.arkivanov.decompose.router.stack.ChildStack - -internal class DefaultStackAnimation( - private val disableInputDuringAnimation: Boolean, - private val selector: (child: Child.Created, otherChild: Child.Created, direction: Direction) -> StackAnimator?, -) : StackAnimation { - - @Composable - override operator fun invoke(stack: ChildStack, modifier: Modifier, content: @Composable (child: Child.Created) -> Unit) { - var activePage by remember { mutableStateOf(stack.activePage()) } - var items by remember { mutableStateOf(getAnimationItems(newPage = activePage, oldPage = null)) } - - if (stack.active.configuration != activePage.child.configuration) { - val oldPage = activePage - activePage = stack.activePage() - items = getAnimationItems(newPage = activePage, oldPage = oldPage) - } - - Box(modifier = modifier) { - items.forEach { (configuration, item) -> - key(configuration) { - Item( - item = item, - onFinished = { direction -> - items = - when (direction) { - Direction.EXIT_FRONT, - Direction.EXIT_BACK -> items - configuration - - Direction.ENTER_FRONT, - Direction.ENTER_BACK -> items + (configuration to AnimationItem.Single(item.child)) - } - }, - content = content, - ) - } - } - - // A workaround until https://issuetracker.google.com/issues/214231672. - // Normally only the exiting child should be disabled. - if (disableInputDuringAnimation && (items.size > 1)) { - Overlay(modifier = Modifier.matchParentSize()) - } - } - } - - @Composable - private fun Item( - item: AnimationItem, - onFinished: (Direction) -> Unit, - content: @Composable (child: Child.Created) -> Unit, - ) { - val childContent = - remember { - movableContentOf { modifier -> - Box(modifier = modifier) { - content(item.child) - } - } - } - - when (item) { - is AnimationItem.Single -> childContent(Modifier) - is AnimationItem.Pair -> ItemPair(item = item, onFinished = onFinished, content = childContent) - } - } - - @Composable - private fun ItemPair( - item: AnimationItem.Pair, - onFinished: (Direction) -> Unit, - content: @Composable (Modifier) -> Unit, - ) { - val direction = item.direction - val animator: StackAnimator? = remember(direction) { selector(item.child, item.otherChild, direction) } - - if (animator == null) { - if (direction.isEnter) { - content(Modifier) - } - - DisposableEffect(Unit) { - onFinished(direction) - onDispose {} - } - } else { - animator( - direction = direction, - onFinished = { onFinished(direction) }, - content = content, - ) - } - } - - @Composable - private fun Overlay(modifier: Modifier) { - Box( - modifier = modifier.pointerInput(Unit) { - awaitPointerEventScope { - while (true) { - val event = awaitPointerEvent() - event.changes.forEach { it.consumeAllChanges() } - } - } - } - ) - } - - private fun ChildStack.activePage(): Page = - Page(child = active, index = items.lastIndex) - - private fun getAnimationItems(newPage: Page, oldPage: Page?): Map> = - when { - oldPage == null -> - listOf(AnimationItem.Single(newPage.child)) - - newPage.index >= oldPage.index -> - listOf( - AnimationItem.Pair(oldPage.child, newPage.child, Direction.EXIT_BACK), - AnimationItem.Pair(newPage.child, oldPage.child, Direction.ENTER_FRONT), - ) - - else -> - listOf( - AnimationItem.Pair(newPage.child, oldPage.child, Direction.ENTER_BACK), - AnimationItem.Pair(oldPage.child, newPage.child, Direction.EXIT_FRONT), - ) - }.associateBy { it.child.configuration } - - private sealed interface AnimationItem { - val child: Child.Created - - data class Single( - override val child: Child.Created, - ) : AnimationItem - - data class Pair( - override val child: Child.Created, - val otherChild: Child.Created, - val direction: Direction, - ) : AnimationItem - } - - private class Page( - val child: Child.Created, - val index: Int, - ) -} diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/DefaultStackAnimator.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/DefaultStackAnimator.kt deleted file mode 100644 index 5190d7f26..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/DefaultStackAnimator.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack.animation - -import androidx.compose.animation.core.AnimationState -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.animateTo -import androidx.compose.animation.core.isFinished -import androidx.compose.animation.core.tween -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier - -internal class DefaultStackAnimator( - private val animationSpec: FiniteAnimationSpec = tween(), - private val frame: @Composable (factor: Float, direction: Direction, content: @Composable (Modifier) -> Unit) -> Unit -) : StackAnimator { - - @Composable - override operator fun invoke(direction: Direction, onFinished: () -> Unit, content: @Composable (Modifier) -> Unit) { - val animationState = remember(direction) { AnimationState(initialValue = 1F) } - - LaunchedEffect(animationState) { - animationState.animateTo( - targetValue = 0F, - animationSpec = animationSpec, - sequentialAnimation = !animationState.isFinished, - ) - - onFinished() - } - - val factor = - when (direction) { - Direction.ENTER_FRONT -> animationState.value - Direction.EXIT_FRONT -> 1F - animationState.value - Direction.ENTER_BACK -> -animationState.value - Direction.EXIT_BACK -> animationState.value - 1F - } - - frame(factor, direction, content) - } -} diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction.kt deleted file mode 100644 index f24d77069..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Direction.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack.animation - -/** - * Represents a direction in which child widgets are animated. - */ -enum class Direction { - - /** - * The child is entering from the front (push). - */ - ENTER_FRONT, - - /** - * The child is exiting to the front (pop). - */ - EXIT_FRONT, - - /** - * The child is entering from the back (move from the backstack). - */ - ENTER_BACK, - - /** - * The child is exiting to the back (move to the backstack). - */ - EXIT_BACK, -} - -val Direction.isEnter: Boolean - get() = - when (this) { - Direction.ENTER_FRONT, - Direction.ENTER_BACK -> true - - Direction.EXIT_FRONT, - Direction.EXIT_BACK -> false - } - -val Direction.isExit: Boolean - get() = !isEnter - -val Direction.isFront: Boolean - get() = - when (this) { - Direction.ENTER_FRONT, - Direction.EXIT_FRONT -> true - - Direction.ENTER_BACK, - Direction.EXIT_BACK -> false - } - -val Direction.isBack: Boolean - get() = !isFront diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/EmptyStackAnimation.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/EmptyStackAnimation.kt deleted file mode 100644 index e8cee56b9..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/EmptyStackAnimation.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack.animation - -import androidx.compose.foundation.layout.Box - -internal fun emptyStackAnimation(): StackAnimation = - StackAnimation { stack, modifier, childContent -> - Box(modifier = modifier) { - childContent(stack.active) - } - } diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Fade.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Fade.kt deleted file mode 100644 index 6700306ca..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Fade.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack.animation - -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.tween -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import kotlin.math.abs - -/** - * A simple fading animation. Appearing children's `alpha` is animated from [minAlpha] to 1.0. - * Disappearing children's `alpha` is animated from 1.0 to [minAlpha]. - */ -fun fade( - animationSpec: FiniteAnimationSpec = tween(), - minAlpha: Float = 0F, -): StackAnimator = - stackAnimator(animationSpec = animationSpec) { factor, _, content -> - content(Modifier.alpha(getFadeAlpha(factor = factor, minAlpha = minAlpha))) - } - -internal fun getFadeAlpha(factor: Float, minAlpha: Float): Float = - (1F - abs(factor) * (1F - minAlpha)).coerceIn(minimumValue = 0F, maximumValue = 1F) diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Scale.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Scale.kt deleted file mode 100644 index ce7c3b83c..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Scale.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack.animation - -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.tween -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.scale - -/** - * A simple scaling animation. Front (above) children are scaling from [frontFactor] to 1.0. - * Back (below) children are scaling from 1.0 to [backFactor]. - */ -fun scale( - animationSpec: FiniteAnimationSpec = tween(), - frontFactor: Float = 1.15F, - backFactor: Float = 0.95F, -): StackAnimator = - stackAnimator(animationSpec = animationSpec) { factor, _, content -> - content( - Modifier.scale( - if (factor >= 0F) { - factor * (frontFactor - 1F) + 1F - } else { - factor * (1F - backFactor) + 1F - } - ) - ) - } diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Slide.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Slide.kt deleted file mode 100644 index ceca3ddb5..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/Slide.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack.animation - -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.tween -import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.layout - -/** - * A simple sliding animation. Children enter from one side and exit to another side. - */ -fun slide(animationSpec: FiniteAnimationSpec = tween()): StackAnimator = - stackAnimator(animationSpec = animationSpec) { factor, _, content -> - content(Modifier.offsetXFactor(factor = factor)) - } - -private fun Modifier.offsetXFactor(factor: Float): Modifier = - layout { measurable, constraints -> - val placeable = measurable.measure(constraints) - - layout(placeable.width, placeable.height) { - placeable.placeRelative(x = (placeable.width.toFloat() * factor).toInt(), y = 0) - } - } diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation.kt deleted file mode 100644 index 3f99416b2..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimation.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack.animation - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.arkivanov.decompose.Child -import com.arkivanov.decompose.router.stack.ChildStack - -/** - * Tracks the [ChildStack] changes and animates between child widget. - */ -fun interface StackAnimation { - - @Composable - operator fun invoke( - stack: ChildStack, - modifier: Modifier, - content: @Composable (child: Child.Created) -> Unit, - ) -} - -/** - * Creates an implementation of [StackAnimation] that allows different [StackAnimator]s. - * - * @param disableInputDuringAnimation disables input and touch events while animating, default value is `true`. - * @param selector provides a [StackAnimator] for current [Child], other [Child] and [Direction]. - */ -fun stackAnimation( - disableInputDuringAnimation: Boolean = true, - selector: (child: Child.Created, otherChild: Child.Created, direction: Direction) -> StackAnimator?, -): StackAnimation = - DefaultStackAnimation( - disableInputDuringAnimation = disableInputDuringAnimation, - selector = selector, - ) - -/** - * Creates an implementation of [StackAnimation] with the provided [StackAnimator]. - * - * @param animator a [StackAnimator] to be used for animation, default is [fade]. - * @param disableInputDuringAnimation disables input and touch events while animating, default value is `true`. - */ -fun stackAnimation( - animator: StackAnimator = fade(), - disableInputDuringAnimation: Boolean = true, -): StackAnimation = - stackAnimation(disableInputDuringAnimation = disableInputDuringAnimation) { _, _, _ -> animator } diff --git a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator.kt b/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator.kt deleted file mode 100644 index 0a55ffb0e..000000000 --- a/extensions-compose-jetpack/src/main/java/com/arkivanov/decompose/extensions/compose/jetpack/stack/animation/StackAnimator.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.arkivanov.decompose.extensions.compose.jetpack.stack.animation - -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.tween -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier - -/** - * Animates a child widget in the given [Direction]. - */ -fun interface StackAnimator { - - /** - * Animates the [content] in the given [Direction], and calls [onFinished] at the end. - */ - @Composable - operator fun invoke( - direction: Direction, - onFinished: () -> Unit, - content: @Composable (Modifier) -> Unit, - ) -} - -/** - * Creates an implementation of [StackAnimator] with a convenient frame-by-frame rendering. - * - * @param animationSpec a [FiniteAnimationSpec] to configure the animation. - * @param frame renders the `content` using the provided `factor` and [Direction]. Called for every animation frame. - * The `factor` argument changes as follows: - * - Always 0F for [Direction.IDLE] - * - From 1F to 0F for [Direction.ENTER_FRONT] - * - From 0F to 1F for [Direction.EXIT_FRONT] - * - From -1F to 0F for [Direction.ENTER_BACK] - * - From 0F to -1F for [Direction.EXIT_BACK] - */ -fun stackAnimator( - animationSpec: FiniteAnimationSpec = tween(), - frame: @Composable (factor: Float, direction: Direction, content: @Composable (Modifier) -> Unit) -> Unit, -): StackAnimator = - DefaultStackAnimator( - animationSpec = animationSpec, - frame = frame - ) - -/** - * Combines (merges) the receiver [StackAnimator] with the [other] [StackAnimator]. - */ -operator fun StackAnimator.plus(other: StackAnimator): StackAnimator = - StackAnimator { direction, onFinished, content -> - val finished = remember(direction) { BooleanArray(2) } - - this( - direction = direction, - onFinished = { - finished[0] = true - if (finished.all { it }) { - onFinished() - } - }, - ) { thisModifier -> - other( - direction = direction, - onFinished = { - finished[1] = true - if (finished.all { it }) { - onFinished() - } - }, - ) { otherModifier -> - content(thisModifier.then(otherModifier)) - } - } - } diff --git a/settings.gradle.kts b/settings.gradle.kts index d148e1b5c..1fc94c054 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,7 +30,6 @@ pluginManagement { if (!startParameter.projectProperties.containsKey("check_publication")) { include(":decompose") - include(":extensions-compose-jetpack") include(":extensions-compose-jetbrains") include(":extensions-android") include(":sample:shared:shared") diff --git a/tools/check-publication/build.gradle.kts b/tools/check-publication/build.gradle.kts index 30f105ef2..4c5a089e2 100644 --- a/tools/check-publication/build.gradle.kts +++ b/tools/check-publication/build.gradle.kts @@ -37,7 +37,6 @@ kotlin { android.main.dependencies { implementation("com.arkivanov.decompose:extensions-android:$version") implementation("com.arkivanov.decompose:extensions-compose-jetbrains:$version") - implementation("com.arkivanov.decompose:extensions-compose-jetpack:$version") } jvm.main.dependencies { From b20d282c1677ff63126842eda40a193ef32a8048 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Wed, 1 Feb 2023 19:16:57 +0000 Subject: [PATCH 03/89] Enabled ios and macos targets for extensions-compose-jetbrains --- deps.versions.toml | 2 +- extensions-compose-jetbrains/build.gradle.kts | 5 +++++ gradle.properties | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 6a5835a7a..35f8d59b3 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "1.0.0-beta-04" +decompose = "1.0.0-beta-04-compose-experimental" kotlin = "1.8.0" essenty = "0.10.0" parcelizeDarwin = "0.1.3" diff --git a/extensions-compose-jetbrains/build.gradle.kts b/extensions-compose-jetbrains/build.gradle.kts index 8a1f7de9f..ef1f40928 100644 --- a/extensions-compose-jetbrains/build.gradle.kts +++ b/extensions-compose-jetbrains/build.gradle.kts @@ -1,4 +1,6 @@ import com.arkivanov.gradle.bundle +import com.arkivanov.gradle.iosCompat +import com.arkivanov.gradle.macosCompat import com.arkivanov.gradle.setupBinaryCompatibilityValidator import com.arkivanov.gradle.setupMultiplatform import com.arkivanov.gradle.setupPublication @@ -14,6 +16,9 @@ plugins { setupMultiplatform { android() jvm() + macosCompat() + iosCompat() + js(IR) { browser() } diff --git a/gradle.properties b/gradle.properties index 20bf3cccc..3c4357e5d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,8 @@ org.gradle.parallel=true org.gradle.caching=true systemProp.org.gradle.internal.publish.checksums.insecure=true kotlin.mpp.androidSourceSetLayoutVersion=2 +org.jetbrains.compose.experimental.uikit.enabled=true +org.jetbrains.compose.experimental.macos.enabled=true # Workaround for https://issuetracker.google.com/issues/185418482 android.experimental.lint.version=8.0.0-alpha10 From 7f8c41f7fbeb933eddf77af4c305331be060ec5f Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Wed, 1 Feb 2023 19:18:04 +0000 Subject: [PATCH 04/89] Added Compose for iOS sample app --- build.gradle.kts | 7 ++ gradle.properties | 7 ++ .../app-darwin-compose}/.gitignore | 0 sample/app-darwin-compose/build.gradle.kts | 87 +++++++++++++++++ .../kotlin/com/arkivanov/sample/app/Main.kt | 93 +++++++++++++++++++ sample/shared/compose/build.gradle.kts | 7 ++ .../shared/utils/AlertDialog.android.kt | 23 +++++ .../sample/shared/dialog/DialogContent.kt | 8 +- .../sample/shared/utils/AlertDialog.kt | 13 +++ .../arkivanov/sample/shared/utils/Vector.kt | 1 + .../customnavigation/KittenContent.ios.kt | 25 +++++ .../feature1/Feature1ContentFactoryIos.kt | 14 +++ .../feature2/Feature2ContentFactoryIos.kt | 14 +++ .../sample/shared/utils/AlertDialog.ios.kt | 78 ++++++++++++++++ .../sample/shared/utils/Orientation.kt | 7 ++ .../sample/shared/utils/AlertDialog.jvm.kt | 23 +++++ .../dynamic-features/api/build.gradle.kts | 4 +- .../compose-api/build.gradle.kts | 2 + .../feature1Impl/build.gradle.kts | 4 +- .../feature2Impl/build.gradle.kts | 5 +- sample/shared/shared/build.gradle.kts | 4 +- settings.gradle.kts | 1 + 22 files changed, 409 insertions(+), 18 deletions(-) rename {extensions-compose-jetpack => sample/app-darwin-compose}/.gitignore (100%) create mode 100644 sample/app-darwin-compose/build.gradle.kts create mode 100644 sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt create mode 100644 sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.android.kt create mode 100644 sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.kt create mode 100644 sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.ios.kt create mode 100644 sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature1/Feature1ContentFactoryIos.kt create mode 100644 sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature2/Feature2ContentFactoryIos.kt create mode 100644 sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt create mode 100644 sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt create mode 100644 sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.jvm.kt diff --git a/build.gradle.kts b/build.gradle.kts index 2586da581..23aedb9c8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -70,4 +70,11 @@ allprojects { google() maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } + + afterEvaluate { + // Workaround for https://youtrack.jetbrains.com/issue/KT-52776 + rootProject.extensions.findByType()?.apply { + versions.webpackCli.version = "4.10.0" + } + } } diff --git a/gradle.properties b/gradle.properties index 3c4357e5d..74ed6ccd9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,3 +12,10 @@ org.jetbrains.compose.experimental.macos.enabled=true # Workaround for https://issuetracker.google.com/issues/185418482 android.experimental.lint.version=8.0.0-alpha10 + +# Workaround for a build failure on CI: +# e: Module "org.jetbrains.compose.runtime:runtime-saveable (org.jetbrains.compose.runtime:runtime-saveable-uikitx64)" +# has a reference to symbol androidx.compose.runtime/remember|-2215966373931868872[0]. Neither the module itself nor +# its dependencies contain such declaration. +# See: https://github.com/JetBrains/compose-jb/issues/2046 +kotlin.native.cacheKind=none diff --git a/extensions-compose-jetpack/.gitignore b/sample/app-darwin-compose/.gitignore similarity index 100% rename from extensions-compose-jetpack/.gitignore rename to sample/app-darwin-compose/.gitignore diff --git a/sample/app-darwin-compose/build.gradle.kts b/sample/app-darwin-compose/build.gradle.kts new file mode 100644 index 000000000..d5762a552 --- /dev/null +++ b/sample/app-darwin-compose/build.gradle.kts @@ -0,0 +1,87 @@ +import com.arkivanov.gradle.bundle +import com.arkivanov.gradle.dependsOn +import com.arkivanov.gradle.setupMultiplatform +import com.arkivanov.gradle.setupSourceSets +import org.jetbrains.compose.experimental.dsl.IOSDevices +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget + +plugins { + id("kotlin-multiplatform") + id("org.jetbrains.compose") + id("com.arkivanov.gradle.setup") +} + +setupMultiplatform(targets = {}) + +kotlin { + listOf(iosX64("uikitX64"), iosArm64("uikitArm64")).forEach { + it.binaries { + executable { + entryPoint = "com.arkivanov.sample.app.main" + freeCompilerArgs += listOf( + "-linker-option", "-framework", "-linker-option", "Metal", + "-linker-option", "-framework", "-linker-option", "CoreText", + "-linker-option", "-framework", "-linker-option", "CoreGraphics" + ) + } + } + } + + setupSourceSets { + val uikit by bundle() + + uikit dependsOn common + iosSet dependsOn uikit + + common.main.dependencies { + implementation(project(":decompose")) + implementation(project(":extensions-compose-jetbrains")) + implementation(project(":sample:shared:shared")) + implementation(project(":sample:shared:compose")) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material) + } + } +} + +kotlin.targets.withType { + binaries.all { + binaryOptions["memoryModel"] = "experimental" + binaryOptions["freezing"] = "disabled" + } +} + +compose.experimental { + uikit.application { + bundleIdPrefix = "com.arkivanov.decompose.sample" + projectName = "DecomposeSample" + + deployConfigurations { + simulator("IPhone12Pro") { + // Usage: ./gradlew iosDeployIPhone12ProDebug + device = IOSDevices.IPHONE_12_PRO + } + + /* + * Usage: ./gradlew iosDeployDeviceDebug + * + * If your are getting "A valid provisioning profile for this executable was not found" error, + * see: https://developer.apple.com/forums/thread/128121?answerId=403323022#403323022 + */ + connectedDevice("Device") { + // Uncomment and fill with your Team ID + // teamId = "" + } + } + } +} + +kotlin { + targets.withType { + binaries.all { + // TODO: the current compose binary surprises LLVM, so disable checks for now. + freeCompilerArgs += "-Xdisable-phases=VerifyBitcode" + } + } +} diff --git a/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt b/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt new file mode 100644 index 000000000..7617efa26 --- /dev/null +++ b/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt @@ -0,0 +1,93 @@ +package com.arkivanov.sample.app + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Application +import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.essenty.lifecycle.LifecycleRegistry +import com.arkivanov.essenty.lifecycle.destroy +import com.arkivanov.essenty.lifecycle.resume +import com.arkivanov.essenty.lifecycle.stop +import com.arkivanov.sample.shared.dynamicfeatures.dynamicfeature.DefaultFeatureInstaller +import com.arkivanov.sample.shared.root.DefaultRootComponent +import com.arkivanov.sample.shared.root.RootContent +import kotlinx.cinterop.ObjCObjectBase +import kotlinx.cinterop.autoreleasepool +import kotlinx.cinterop.cstr +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.toCValues +import platform.Foundation.NSStringFromClass +import platform.UIKit.UIApplication +import platform.UIKit.UIApplicationDelegateProtocol +import platform.UIKit.UIApplicationDelegateProtocolMeta +import platform.UIKit.UIApplicationMain +import platform.UIKit.UIResponder +import platform.UIKit.UIResponderMeta +import platform.UIKit.UIScreen +import platform.UIKit.UIWindow + +fun main() { + val args = emptyArray() + memScoped { + val argc = args.size + 1 + val argv = (arrayOf("skikoApp") + args).map { it.cstr.ptr }.toCValues() + autoreleasepool { + UIApplicationMain(argc, argv, null, NSStringFromClass(SkikoAppDelegate)) + } + } +} + +class SkikoAppDelegate : UIResponder, UIApplicationDelegateProtocol { + companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta + + @ObjCObjectBase.OverrideInit + constructor() : super() + + private val lifecycle = LifecycleRegistry() + + private val root = + DefaultRootComponent( + componentContext = DefaultComponentContext(lifecycle = lifecycle), + featureInstaller = DefaultFeatureInstaller, + ) + + private var _window: UIWindow? = null + override fun window() = _window + override fun setWindow(window: UIWindow?) { + _window = window + } + + override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map?): Boolean { + window = UIWindow(frame = UIScreen.mainScreen.bounds) + window!!.rootViewController = Application("Minesweeper") { + Column { + // To skip upper part of screen. + Box(modifier = Modifier.height(100.dp)) + + RootContent( + component = root, + modifier = Modifier.weight(1F).fillMaxWidth(), + ) + } + } + window!!.makeKeyAndVisible() + return true + } + + override fun applicationDidBecomeActive(application: UIApplication) { + lifecycle.resume() + } + + override fun applicationWillResignActive(application: UIApplication) { + lifecycle.stop() + + } + + override fun applicationWillTerminate(application: UIApplication) { + lifecycle.destroy() + } +} diff --git a/sample/shared/compose/build.gradle.kts b/sample/shared/compose/build.gradle.kts index f9e982635..e84ddeb6f 100644 --- a/sample/shared/compose/build.gradle.kts +++ b/sample/shared/compose/build.gradle.kts @@ -1,4 +1,6 @@ import com.arkivanov.gradle.bundle +import com.arkivanov.gradle.dependsOn +import com.arkivanov.gradle.iosCompat import com.arkivanov.gradle.setupMultiplatform import com.arkivanov.gradle.setupSourceSets @@ -12,6 +14,7 @@ plugins { setupMultiplatform { android() jvm() + iosCompat() } android { @@ -21,6 +24,10 @@ android { kotlin { setupSourceSets { val jvm by bundle() + val ios by bundle() + + ios dependsOn common + iosSet dependsOn ios common.main.dependencies { implementation(project(":decompose")) diff --git a/sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.android.kt b/sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.android.kt new file mode 100644 index 000000000..58afbb7b8 --- /dev/null +++ b/sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.android.kt @@ -0,0 +1,23 @@ +package com.arkivanov.sample.shared.utils + +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@OptIn(ExperimentalMaterialApi::class) +@Composable +internal actual fun AlertDialog( + onDismissRequest: () -> Unit, + confirmButton: @Composable () -> Unit, + modifier: Modifier, + title: @Composable () -> Unit, + text: @Composable () -> Unit, +) { + androidx.compose.material.AlertDialog( + onDismissRequest = onDismissRequest, + confirmButton = confirmButton, + modifier = modifier, + title = title, + text = text, + ) +} diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dialog/DialogContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dialog/DialogContent.kt index f756b8c2d..31df7d6bc 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dialog/DialogContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dialog/DialogContent.kt @@ -1,15 +1,13 @@ package com.arkivanov.sample.shared.dialog -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.material.Text -import androidx.compose.material.AlertDialog -import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.arkivanov.sample.shared.utils.AlertDialog -@OptIn(ExperimentalMaterialApi::class) @Composable fun DialogContent(dialogComponent: DialogComponent) { AlertDialog( @@ -31,6 +29,6 @@ fun DialogContent(dialogComponent: DialogComponent) { Text("Dismiss") } }, - modifier = Modifier.width(300.dp), + modifier = Modifier.widthIn(min = 200.dp), ) } diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.kt new file mode 100644 index 000000000..846ca5b50 --- /dev/null +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.kt @@ -0,0 +1,13 @@ +package com.arkivanov.sample.shared.utils + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +internal expect fun AlertDialog( + onDismissRequest: () -> Unit, + confirmButton: @Composable () -> Unit, + modifier: Modifier, + title: @Composable () -> Unit, + text: @Composable () -> Unit, +) diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/Vector.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/Vector.kt index 5c080a658..fa4e59696 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/Vector.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/Vector.kt @@ -1,5 +1,6 @@ package com.arkivanov.sample.shared.utils +import kotlin.jvm.JvmInline import kotlin.math.PI import kotlin.math.cos import kotlin.math.sin diff --git a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.ios.kt b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.ios.kt new file mode 100644 index 000000000..28942fe4a --- /dev/null +++ b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.ios.kt @@ -0,0 +1,25 @@ +package com.arkivanov.sample.shared.customnavigation + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AccountBox +import androidx.compose.material.icons.filled.Call +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.Info +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import com.arkivanov.sample.shared.customnavigation.KittenComponent.ImageType + +@Composable +internal actual fun getKittenPainter(imageType: ImageType): Painter = + // TODO: Use resources with kitten photos when supported by Compose for iOS + rememberVectorPainter( + when (imageType) { + ImageType.CAT_1 -> Icons.Default.AccountBox + ImageType.CAT_2 -> Icons.Default.Info + ImageType.CAT_3 -> Icons.Default.Call + ImageType.CAT_4 -> Icons.Default.Check + ImageType.CAT_5 -> Icons.Default.Clear + } + ) diff --git a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature1/Feature1ContentFactoryIos.kt b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature1/Feature1ContentFactoryIos.kt new file mode 100644 index 000000000..0a0b40b9f --- /dev/null +++ b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature1/Feature1ContentFactoryIos.kt @@ -0,0 +1,14 @@ +package com.arkivanov.sample.shared.dynamicfeatures.feature1 + +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.arkivanov.sample.shared.dynamicfeatures.DynamicFeatureContent + +internal actual fun feature1Content(): DynamicFeatureContent = + object : DynamicFeatureContent { + @Composable + override fun invoke(component: Feature1, modifier: Modifier) { + Text(text = "Not implemented") + } + } diff --git a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature2/Feature2ContentFactoryIos.kt b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature2/Feature2ContentFactoryIos.kt new file mode 100644 index 000000000..701434e3a --- /dev/null +++ b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature2/Feature2ContentFactoryIos.kt @@ -0,0 +1,14 @@ +package com.arkivanov.sample.shared.dynamicfeatures.feature2 + +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.arkivanov.sample.shared.dynamicfeatures.DynamicFeatureContent + +internal actual fun feature2Content(): DynamicFeatureContent = + object : DynamicFeatureContent { + @Composable + override fun invoke(component: Feature2, modifier: Modifier) { + Text(text = "Not implemented") + } + } diff --git a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt new file mode 100644 index 000000000..7cc024de5 --- /dev/null +++ b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt @@ -0,0 +1,78 @@ +package com.arkivanov.sample.shared.utils + +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.ContentAlpha +import androidx.compose.material.LocalContentAlpha +import androidx.compose.material.MaterialTheme +import androidx.compose.material.ProvideTextStyle +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntRect +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Popup +import androidx.compose.ui.window.PopupPositionProvider + +@Composable +internal actual fun AlertDialog( + onDismissRequest: () -> Unit, + confirmButton: @Composable () -> Unit, + modifier: Modifier, + title: @Composable () -> Unit, + text: @Composable () -> Unit, +) { + Popup( + popupPositionProvider = object : PopupPositionProvider { + override fun calculatePosition( + anchorBounds: IntRect, + windowSize: IntSize, + layoutDirection: LayoutDirection, + popupContentSize: IntSize + ): IntOffset = IntOffset.Zero + }, + focusable = true, + onDismissRequest = onDismissRequest, + ) { + Box( + modifier = Modifier + .fillMaxSize() + .pointerInput(onDismissRequest) { + detectTapGestures(onPress = { onDismissRequest() }) + }, + contentAlignment = Alignment.Center + ) { + Surface( + elevation = 24.dp, + modifier = modifier.align(Alignment.Center), + ) { + Column { + Box(modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 24.dp)) { + CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { + ProvideTextStyle(MaterialTheme.typography.subtitle1, title) + } + } + + Box(modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 24.dp)) { + CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { + ProvideTextStyle(MaterialTheme.typography.body2, text) + } + } + + Box(modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp).align(Alignment.End)) { + confirmButton() + } + } + } + } + } +} diff --git a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt new file mode 100644 index 000000000..b15bdf7a0 --- /dev/null +++ b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt @@ -0,0 +1,7 @@ +package com.arkivanov.sample.shared.utils + +import androidx.compose.runtime.Composable + +internal actual val orientation: Orientation + @Composable + get() = Orientation.PORTRAIT diff --git a/sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.jvm.kt b/sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.jvm.kt new file mode 100644 index 000000000..58afbb7b8 --- /dev/null +++ b/sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.jvm.kt @@ -0,0 +1,23 @@ +package com.arkivanov.sample.shared.utils + +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@OptIn(ExperimentalMaterialApi::class) +@Composable +internal actual fun AlertDialog( + onDismissRequest: () -> Unit, + confirmButton: @Composable () -> Unit, + modifier: Modifier, + title: @Composable () -> Unit, + text: @Composable () -> Unit, +) { + androidx.compose.material.AlertDialog( + onDismissRequest = onDismissRequest, + confirmButton = confirmButton, + modifier = modifier, + title = title, + text = text, + ) +} diff --git a/sample/shared/dynamic-features/api/build.gradle.kts b/sample/shared/dynamic-features/api/build.gradle.kts index 9dfaa7e25..46de64d4d 100644 --- a/sample/shared/dynamic-features/api/build.gradle.kts +++ b/sample/shared/dynamic-features/api/build.gradle.kts @@ -11,9 +11,7 @@ setupMultiplatform { android() jvm() js(IR) { browser() } - iosCompat( - arm64 = null, // Comment out to enable arm64 target - ) + iosCompat() } android { diff --git a/sample/shared/dynamic-features/compose-api/build.gradle.kts b/sample/shared/dynamic-features/compose-api/build.gradle.kts index eb9e0552e..b52d9d7e2 100644 --- a/sample/shared/dynamic-features/compose-api/build.gradle.kts +++ b/sample/shared/dynamic-features/compose-api/build.gradle.kts @@ -1,3 +1,4 @@ +import com.arkivanov.gradle.iosCompat import com.arkivanov.gradle.setupMultiplatform import com.arkivanov.gradle.setupSourceSets @@ -11,6 +12,7 @@ plugins { setupMultiplatform { android() jvm() + iosCompat() } android { diff --git a/sample/shared/dynamic-features/feature1Impl/build.gradle.kts b/sample/shared/dynamic-features/feature1Impl/build.gradle.kts index 0df853284..1e72db0d7 100644 --- a/sample/shared/dynamic-features/feature1Impl/build.gradle.kts +++ b/sample/shared/dynamic-features/feature1Impl/build.gradle.kts @@ -18,9 +18,7 @@ setupMultiplatform { android() jvm() js(IR) { browser() } - iosCompat( - arm64 = null // Comment out to enable arm64 target - ) + iosCompat() } android { diff --git a/sample/shared/dynamic-features/feature2Impl/build.gradle.kts b/sample/shared/dynamic-features/feature2Impl/build.gradle.kts index fb6189f2d..01b9591c9 100644 --- a/sample/shared/dynamic-features/feature2Impl/build.gradle.kts +++ b/sample/shared/dynamic-features/feature2Impl/build.gradle.kts @@ -18,9 +18,7 @@ setupMultiplatform { android() jvm() js(IR) { browser() } - iosCompat( - arm64 = null // Comment out to enable arm64 target - ) + iosCompat() } android { @@ -62,7 +60,6 @@ kotlin { compose.web.targets() - plugins.removeAll { it is ComposeCompilerKotlinSupportPlugin } class ComposeNoNativePlugin : KotlinCompilerPluginSupportPlugin by ComposeCompilerKotlinSupportPlugin() { diff --git a/sample/shared/shared/build.gradle.kts b/sample/shared/shared/build.gradle.kts index 2988c465a..2f31eab6f 100644 --- a/sample/shared/shared/build.gradle.kts +++ b/sample/shared/shared/build.gradle.kts @@ -18,9 +18,7 @@ setupMultiplatform { android() jvm() js(IR) { browser() } - iosCompat( - arm64 = null, // Comment out to enable arm64 target - ) + iosCompat() } android { diff --git a/settings.gradle.kts b/settings.gradle.kts index 1fc94c054..8504ca340 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -40,6 +40,7 @@ if (!startParameter.projectProperties.containsKey("check_publication")) { include(":sample:shared:dynamic-features:feature2Impl") include(":sample:app-android") include(":sample:app-desktop") + include(":sample:app-darwin-compose") include(":sample:app-js") } else { include(":tools:check-publication") From 26f127ab31be849a546f704055219d07810ca1e2 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Wed, 1 Feb 2023 22:29:10 +0000 Subject: [PATCH 05/89] Added Compose for Web sample app --- .../gradle.properties | 1 - .../stack/animation/EmptyStackAnimation.kt | 23 +++++- .../stack/animation/StackAnimator.kt | 24 +++++- gradle.properties | 1 + sample/app-darwin-compose/build.gradle.kts | 6 +- sample/app-js-compose/.gitignore | 1 + sample/app-js-compose/build.gradle.kts | 36 +++++++++ .../arkivanov/decompose/sample/app/Main.kt | 49 ++++++++++++ .../src/jsMain/resources/index.html | 16 ++++ sample/shared/compose/build.gradle.kts | 2 + .../sample/shared/utils/Orientation.kt | 14 ---- .../sample/shared/root/RootContent.kt | 17 +++- .../sample/shared/utils/Orientation.kt | 9 --- .../sample/shared/utils/Orientation.kt | 7 -- .../customnavigation/KittenContent.js.kt | 25 ++++++ .../feature1/Feature1ContentFactoryIos.kt | 14 ++++ .../feature2/Feature2ContentFactoryIos.kt | 14 ++++ .../sample/shared/utils/AlertDialog.ios.kt | 78 +++++++++++++++++++ .../sample/shared/utils/Orientation.kt | 7 -- .../compose-api/build.gradle.kts | 1 + settings.gradle.kts | 1 + 21 files changed, 298 insertions(+), 48 deletions(-) delete mode 100644 extensions-compose-jetbrains/gradle.properties create mode 100644 sample/app-js-compose/.gitignore create mode 100644 sample/app-js-compose/build.gradle.kts create mode 100644 sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt create mode 100644 sample/app-js-compose/src/jsMain/resources/index.html delete mode 100644 sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt delete mode 100644 sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt delete mode 100644 sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt create mode 100644 sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.js.kt create mode 100644 sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature1/Feature1ContentFactoryIos.kt create mode 100644 sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature2/Feature2ContentFactoryIos.kt create mode 100644 sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt delete mode 100644 sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt diff --git a/extensions-compose-jetbrains/gradle.properties b/extensions-compose-jetbrains/gradle.properties deleted file mode 100644 index 9c8f6b37b..000000000 --- a/extensions-compose-jetbrains/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -org.jetbrains.compose.experimental.jscanvas.enabled=true diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/EmptyStackAnimation.kt b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/EmptyStackAnimation.kt index aa5090e06..4220c05ef 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/EmptyStackAnimation.kt +++ b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/EmptyStackAnimation.kt @@ -1,10 +1,29 @@ package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.Child +import com.arkivanov.decompose.router.stack.ChildStack internal fun emptyStackAnimation(): StackAnimation = - StackAnimation { stack, modifier, childContent -> + EmptyStackAnimation() + +/* + * Can't be anonymous. See: + * https://github.com/JetBrains/compose-jb/issues/2688 + * https://github.com/JetBrains/compose-jb/issues/2612 + */ +private class EmptyStackAnimation : StackAnimation { + + @Composable + override fun invoke( + stack: ChildStack, + modifier: Modifier, + content: @Composable (child: Child.Created) -> Unit, + ) { Box(modifier = modifier) { - childContent(stack.active) + content(stack.active) } } +} diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator.kt b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator.kt index a2fc03b73..2ff00d067 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator.kt +++ b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator.kt @@ -46,10 +46,27 @@ fun stackAnimator( * Combines (merges) the receiver [StackAnimator] with the [other] [StackAnimator]. */ operator fun StackAnimator.plus(other: StackAnimator): StackAnimator = - StackAnimator { direction, onFinished, content -> + PlusStackAnimator(first = this, second = other) + +/* + * Can't be anonymous. See: + * https://github.com/JetBrains/compose-jb/issues/2688 + * https://github.com/JetBrains/compose-jb/issues/2612 + */ +private class PlusStackAnimator( + private val first: StackAnimator, + private val second: StackAnimator, +) : StackAnimator { + + @Composable + override fun invoke( + direction: Direction, + onFinished: () -> Unit, + content: @Composable (Modifier) -> Unit, + ) { val finished = remember(direction) { BooleanArray(2) } - this( + first( direction = direction, onFinished = { finished[0] = true @@ -58,7 +75,7 @@ operator fun StackAnimator.plus(other: StackAnimator): StackAnimator = } }, ) { thisModifier -> - other( + second( direction = direction, onFinished = { finished[1] = true @@ -71,3 +88,4 @@ operator fun StackAnimator.plus(other: StackAnimator): StackAnimator = } } } +} diff --git a/gradle.properties b/gradle.properties index 74ed6ccd9..e5278907d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,7 @@ systemProp.org.gradle.internal.publish.checksums.insecure=true kotlin.mpp.androidSourceSetLayoutVersion=2 org.jetbrains.compose.experimental.uikit.enabled=true org.jetbrains.compose.experimental.macos.enabled=true +org.jetbrains.compose.experimental.jscanvas.enabled=true # Workaround for https://issuetracker.google.com/issues/185418482 android.experimental.lint.version=8.0.0-alpha10 diff --git a/sample/app-darwin-compose/build.gradle.kts b/sample/app-darwin-compose/build.gradle.kts index d5762a552..661a93efa 100644 --- a/sample/app-darwin-compose/build.gradle.kts +++ b/sample/app-darwin-compose/build.gradle.kts @@ -11,9 +11,7 @@ plugins { id("com.arkivanov.gradle.setup") } -setupMultiplatform(targets = {}) - -kotlin { +setupMultiplatform { listOf(iosX64("uikitX64"), iosArm64("uikitArm64")).forEach { it.binaries { executable { @@ -26,7 +24,9 @@ kotlin { } } } +} +kotlin { setupSourceSets { val uikit by bundle() diff --git a/sample/app-js-compose/.gitignore b/sample/app-js-compose/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/sample/app-js-compose/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/sample/app-js-compose/build.gradle.kts b/sample/app-js-compose/build.gradle.kts new file mode 100644 index 000000000..ae109ce2c --- /dev/null +++ b/sample/app-js-compose/build.gradle.kts @@ -0,0 +1,36 @@ +import com.arkivanov.gradle.bundle +import com.arkivanov.gradle.setupMultiplatform +import com.arkivanov.gradle.setupSourceSets + +plugins { + id("kotlin-multiplatform") + id("org.jetbrains.compose") + id("com.arkivanov.gradle.setup") +} + +setupMultiplatform { + js(IR) { + browser() + binaries.executable() + } +} + +kotlin { + setupSourceSets { + val js by bundle() + + js.main.dependencies { + implementation(project(":decompose")) + implementation(project(":extensions-compose-jetbrains")) + implementation(project(":sample:shared:shared")) + implementation(project(":sample:shared:compose")) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material) + } + } +} + +compose.experimental { + web.application {} +} diff --git a/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt b/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt new file mode 100644 index 000000000..fef1ffef4 --- /dev/null +++ b/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt @@ -0,0 +1,49 @@ +package com.arkivanov.decompose.sample.app + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.Modifier +import androidx.compose.ui.window.Window +import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.essenty.lifecycle.LifecycleRegistry +import com.arkivanov.essenty.lifecycle.resume +import com.arkivanov.essenty.lifecycle.stop +import com.arkivanov.sample.shared.dynamicfeatures.dynamicfeature.DefaultFeatureInstaller +import com.arkivanov.sample.shared.root.DefaultRootComponent +import com.arkivanov.sample.shared.root.RootContent +import org.jetbrains.skiko.wasm.onWasmReady +import web.dom.DocumentVisibilityState +import web.dom.document +import web.events.EventType + +fun main() { + val lifecycle = LifecycleRegistry() + + val root = + DefaultRootComponent( + componentContext = DefaultComponentContext(lifecycle = lifecycle), + featureInstaller = DefaultFeatureInstaller, + ) + + lifecycle.attachToDocument() + + onWasmReady { + Window("Decompose Sample") { + RootContent(component = root, modifier = Modifier.fillMaxSize()) + } + } +} + +private fun LifecycleRegistry.attachToDocument() { + fun onVisibilityChanged() { + if (document.visibilityState == DocumentVisibilityState.visible) { + resume() + } else { + stop() + } + } + + onVisibilityChanged() + + document.addEventListener(type = EventType("visibilitychange"), callback = { onVisibilityChanged() }) +} + diff --git a/sample/app-js-compose/src/jsMain/resources/index.html b/sample/app-js-compose/src/jsMain/resources/index.html new file mode 100644 index 000000000..97f09da55 --- /dev/null +++ b/sample/app-js-compose/src/jsMain/resources/index.html @@ -0,0 +1,16 @@ + + + + + Title + + + +

Decompose Sample

+
+ +
+ + + + diff --git a/sample/shared/compose/build.gradle.kts b/sample/shared/compose/build.gradle.kts index e84ddeb6f..9383b512b 100644 --- a/sample/shared/compose/build.gradle.kts +++ b/sample/shared/compose/build.gradle.kts @@ -15,6 +15,7 @@ setupMultiplatform { android() jvm() iosCompat() + js(IR) { browser() } } android { @@ -38,6 +39,7 @@ kotlin { implementation(compose.runtime) implementation(compose.foundation) implementation(compose.material) + implementation(compose.ui) } jvm.main.dependencies { diff --git a/sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt b/sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt deleted file mode 100644 index 12abed508..000000000 --- a/sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.arkivanov.sample.shared.utils - -import android.content.res.Configuration -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext - -internal actual val orientation: Orientation - @Composable - get() = - if (LocalContext.current.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { - Orientation.LANDSCAPE - } else { - Orientation.PORTRAIT - } diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt index 2b518a2fc..530abd2a6 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt @@ -129,13 +129,26 @@ private val Child.index: Int } private fun StackAnimator.flipSide(): StackAnimator = - StackAnimator { direction, onFinished, content -> - invoke( + FlipSideStackAnimator(animator = this) + +/* + * Can't be anonymous. See: + * https://github.com/JetBrains/compose-jb/issues/2688 + * https://github.com/JetBrains/compose-jb/issues/2612 + */ +private class FlipSideStackAnimator( + private val animator: StackAnimator, +) : StackAnimator { + + @Composable + override fun invoke(direction: Direction, onFinished: () -> Unit, content: @Composable (Modifier) -> Unit) { + animator( direction = direction.flipSide(), onFinished = onFinished, content = content, ) } +} @Suppress("OPT_IN_USAGE") private fun Direction.flipSide(): Direction = diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt deleted file mode 100644 index f820a132a..000000000 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.arkivanov.sample.shared.utils - -import androidx.compose.runtime.Composable - -internal expect val orientation: Orientation @Composable get - -internal enum class Orientation { - PORTRAIT, LANDSCAPE -} diff --git a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt deleted file mode 100644 index b15bdf7a0..000000000 --- a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.arkivanov.sample.shared.utils - -import androidx.compose.runtime.Composable - -internal actual val orientation: Orientation - @Composable - get() = Orientation.PORTRAIT diff --git a/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.js.kt b/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.js.kt new file mode 100644 index 000000000..e4af9d985 --- /dev/null +++ b/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.js.kt @@ -0,0 +1,25 @@ +package com.arkivanov.sample.shared.customnavigation + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AccountBox +import androidx.compose.material.icons.filled.Call +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.Info +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import com.arkivanov.sample.shared.customnavigation.KittenComponent.ImageType + +@Composable +internal actual fun getKittenPainter(imageType: ImageType): Painter = + // TODO: Use resources with kitten photos when supported by Compose for Web + rememberVectorPainter( + when (imageType) { + ImageType.CAT_1 -> Icons.Default.AccountBox + ImageType.CAT_2 -> Icons.Default.Info + ImageType.CAT_3 -> Icons.Default.Call + ImageType.CAT_4 -> Icons.Default.Check + ImageType.CAT_5 -> Icons.Default.Clear + } + ) diff --git a/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature1/Feature1ContentFactoryIos.kt b/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature1/Feature1ContentFactoryIos.kt new file mode 100644 index 000000000..0a0b40b9f --- /dev/null +++ b/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature1/Feature1ContentFactoryIos.kt @@ -0,0 +1,14 @@ +package com.arkivanov.sample.shared.dynamicfeatures.feature1 + +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.arkivanov.sample.shared.dynamicfeatures.DynamicFeatureContent + +internal actual fun feature1Content(): DynamicFeatureContent = + object : DynamicFeatureContent { + @Composable + override fun invoke(component: Feature1, modifier: Modifier) { + Text(text = "Not implemented") + } + } diff --git a/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature2/Feature2ContentFactoryIos.kt b/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature2/Feature2ContentFactoryIos.kt new file mode 100644 index 000000000..701434e3a --- /dev/null +++ b/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/feature2/Feature2ContentFactoryIos.kt @@ -0,0 +1,14 @@ +package com.arkivanov.sample.shared.dynamicfeatures.feature2 + +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.arkivanov.sample.shared.dynamicfeatures.DynamicFeatureContent + +internal actual fun feature2Content(): DynamicFeatureContent = + object : DynamicFeatureContent { + @Composable + override fun invoke(component: Feature2, modifier: Modifier) { + Text(text = "Not implemented") + } + } diff --git a/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt b/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt new file mode 100644 index 000000000..7cc024de5 --- /dev/null +++ b/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt @@ -0,0 +1,78 @@ +package com.arkivanov.sample.shared.utils + +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.ContentAlpha +import androidx.compose.material.LocalContentAlpha +import androidx.compose.material.MaterialTheme +import androidx.compose.material.ProvideTextStyle +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntRect +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Popup +import androidx.compose.ui.window.PopupPositionProvider + +@Composable +internal actual fun AlertDialog( + onDismissRequest: () -> Unit, + confirmButton: @Composable () -> Unit, + modifier: Modifier, + title: @Composable () -> Unit, + text: @Composable () -> Unit, +) { + Popup( + popupPositionProvider = object : PopupPositionProvider { + override fun calculatePosition( + anchorBounds: IntRect, + windowSize: IntSize, + layoutDirection: LayoutDirection, + popupContentSize: IntSize + ): IntOffset = IntOffset.Zero + }, + focusable = true, + onDismissRequest = onDismissRequest, + ) { + Box( + modifier = Modifier + .fillMaxSize() + .pointerInput(onDismissRequest) { + detectTapGestures(onPress = { onDismissRequest() }) + }, + contentAlignment = Alignment.Center + ) { + Surface( + elevation = 24.dp, + modifier = modifier.align(Alignment.Center), + ) { + Column { + Box(modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 24.dp)) { + CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { + ProvideTextStyle(MaterialTheme.typography.subtitle1, title) + } + } + + Box(modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 24.dp)) { + CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { + ProvideTextStyle(MaterialTheme.typography.body2, text) + } + } + + Box(modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp).align(Alignment.End)) { + confirmButton() + } + } + } + } + } +} diff --git a/sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt b/sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt deleted file mode 100644 index 966acfc6e..000000000 --- a/sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/Orientation.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.arkivanov.sample.shared.utils - -import androidx.compose.runtime.Composable - -internal actual val orientation: Orientation - @Composable - get() = Orientation.LANDSCAPE diff --git a/sample/shared/dynamic-features/compose-api/build.gradle.kts b/sample/shared/dynamic-features/compose-api/build.gradle.kts index b52d9d7e2..d6d7494fe 100644 --- a/sample/shared/dynamic-features/compose-api/build.gradle.kts +++ b/sample/shared/dynamic-features/compose-api/build.gradle.kts @@ -13,6 +13,7 @@ setupMultiplatform { android() jvm() iosCompat() + js(IR) { browser() } } android { diff --git a/settings.gradle.kts b/settings.gradle.kts index 8504ca340..21650ff1d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -41,6 +41,7 @@ if (!startParameter.projectProperties.containsKey("check_publication")) { include(":sample:app-android") include(":sample:app-desktop") include(":sample:app-darwin-compose") + include(":sample:app-js-compose") include(":sample:app-js") } else { include(":tools:check-publication") From a04e3c1413f234413afa9525e10ec78b0e005c72 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Mon, 29 May 2023 18:41:39 +0100 Subject: [PATCH 06/89] Add Predictive Back Gesture Overlay to iOS Compose sample --- .../kotlin/com/arkivanov/sample/app/Main.kt | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt b/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt index 7617efa26..5e33d9c22 100644 --- a/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt +++ b/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt @@ -2,12 +2,17 @@ package com.arkivanov.sample.app import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Application import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.extensions.compose.jetbrains.PredictiveBackGestureIcon +import com.arkivanov.decompose.extensions.compose.jetbrains.PredictiveBackGestureOverlay +import com.arkivanov.essenty.backhandler.BackDispatcher import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.destroy import com.arkivanov.essenty.lifecycle.resume @@ -47,11 +52,12 @@ class SkikoAppDelegate : UIResponder, UIApplicationDelegateProtocol { @ObjCObjectBase.OverrideInit constructor() : super() + private val backDispatcher = BackDispatcher() private val lifecycle = LifecycleRegistry() private val root = DefaultRootComponent( - componentContext = DefaultComponentContext(lifecycle = lifecycle), + componentContext = DefaultComponentContext(lifecycle = lifecycle, backHandler = backDispatcher), featureInstaller = DefaultFeatureInstaller, ) @@ -63,15 +69,33 @@ class SkikoAppDelegate : UIResponder, UIApplicationDelegateProtocol { override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map?): Boolean { window = UIWindow(frame = UIScreen.mainScreen.bounds) + +// backDispatcher.register( +// BackCallback { +// window!!.hidden = true +// } +// ) + window!!.rootViewController = Application("Minesweeper") { Column { // To skip upper part of screen. - Box(modifier = Modifier.height(100.dp)) + Box(modifier = Modifier.height(64.dp)) - RootContent( - component = root, - modifier = Modifier.weight(1F).fillMaxWidth(), - ) + PredictiveBackGestureOverlay( + backDispatcher = backDispatcher, + backIcon = { progress, _ -> + PredictiveBackGestureIcon( + imageVector = Icons.Default.ArrowBack, + progress = progress, + ) + }, + modifier = Modifier.fillMaxSize(), + ) { + RootContent( + component = root, + modifier = Modifier.fillMaxSize(), + ) + } } } window!!.makeKeyAndVisible() From b6f3f2b01d4f7bc2f21e6aa7f1790c7c0d967584 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 9 Jun 2023 11:16:46 +0100 Subject: [PATCH 07/89] Bumped version to 2.1.0-compose-experimental-alpha-02 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 23199b6e5..46d18c542 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.1.0-compose-experimental-alpha-01" +decompose = "2.1.0-compose-experimental-alpha-02" kotlin = "1.8.20" essenty = "1.2.0-alpha-02" parcelizeDarwin = "0.1.4" From e6b80c2beaee5b2a9251aaf021c27496724d65fa Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 2 Jul 2023 12:34:17 +0100 Subject: [PATCH 08/89] Bumped version to 2.1.0-compose-experimental-alpha-03 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 46d18c542..06477767c 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.1.0-compose-experimental-alpha-02" +decompose = "2.1.0-compose-experimental-alpha-03" kotlin = "1.8.20" essenty = "1.2.0-alpha-02" parcelizeDarwin = "0.1.4" From 4fb417a6de228d7d46d6fad57573479adb8c6892 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 17 Aug 2023 17:01:52 +0100 Subject: [PATCH 09/89] Update gradle scripts for Kotlin 1.9.0 --- extensions-compose-jetbrains/build.gradle.kts | 5 +---- sample/app-js-compose/build.gradle.kts | 2 +- sample/shared/compose/build.gradle.kts | 2 +- sample/shared/dynamic-features/compose-api/build.gradle.kts | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/extensions-compose-jetbrains/build.gradle.kts b/extensions-compose-jetbrains/build.gradle.kts index 8f7bb1298..c79b232b8 100644 --- a/extensions-compose-jetbrains/build.gradle.kts +++ b/extensions-compose-jetbrains/build.gradle.kts @@ -19,10 +19,7 @@ setupMultiplatform { jvm() macosCompat() iosCompat() - - js(IR) { - browser() - } + js { browser() } } setupPublication() diff --git a/sample/app-js-compose/build.gradle.kts b/sample/app-js-compose/build.gradle.kts index ae109ce2c..5771bf9bd 100644 --- a/sample/app-js-compose/build.gradle.kts +++ b/sample/app-js-compose/build.gradle.kts @@ -9,7 +9,7 @@ plugins { } setupMultiplatform { - js(IR) { + js { browser() binaries.executable() } diff --git a/sample/shared/compose/build.gradle.kts b/sample/shared/compose/build.gradle.kts index fffe9f049..48ee55122 100644 --- a/sample/shared/compose/build.gradle.kts +++ b/sample/shared/compose/build.gradle.kts @@ -15,7 +15,7 @@ setupMultiplatform { androidTarget() jvm() iosCompat() - js(IR) { browser() } + js { browser() } } android { diff --git a/sample/shared/dynamic-features/compose-api/build.gradle.kts b/sample/shared/dynamic-features/compose-api/build.gradle.kts index 6e91bd9f6..9a8354ecd 100644 --- a/sample/shared/dynamic-features/compose-api/build.gradle.kts +++ b/sample/shared/dynamic-features/compose-api/build.gradle.kts @@ -13,7 +13,7 @@ setupMultiplatform { androidTarget() jvm() iosCompat() - js(IR) { browser() } + js { browser() } } android { From 79fee723801139ec677b22dc00d2d439737653b8 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Wed, 16 Aug 2023 23:18:38 +0100 Subject: [PATCH 10/89] Updated Compose for iOS sample for Kotlin 1.9.0 --- kotlin-js-store/yarn.lock | 77 ++++++++++--------- .../kotlin/com/arkivanov/sample/app/Main.kt | 20 ++--- 2 files changed, 46 insertions(+), 51 deletions(-) diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index e9e877521..5007af391 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -854,20 +854,22 @@ "@webassemblyjs/ast" "1.11.6" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^2.1.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" - integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== +"@webpack-cli/configtest@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== -"@webpack-cli/info@^2.0.1": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" - integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== +"@webpack-cli/info@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== + dependencies: + envinfo "^7.7.3" -"@webpack-cli/serve@^2.0.3": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" - integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== +"@webpack-cli/serve@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -1295,16 +1297,16 @@ colorette@^2.0.14: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== -commander@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" - integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== - commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -2122,10 +2124,10 @@ inline-style-prefixer@^7.0.0: css-in-js-utils "^3.1.0" fast-loops "^1.1.3" -interpret@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" - integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== ipaddr.js@1.9.1: version "1.9.1" @@ -2934,12 +2936,12 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -rechoir@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" - integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== +rechoir@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" + integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== dependencies: - resolve "^1.20.0" + resolve "^1.9.0" regenerator-runtime@^0.13.11: version "0.13.11" @@ -2992,7 +2994,7 @@ resolve@^1.19.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.20.0: +resolve@^1.9.0: version "1.22.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== @@ -3519,23 +3521,22 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -webpack-cli@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.0.tgz#abc4b1f44b50250f2632d8b8b536cfe2f6257891" - integrity sha512-a7KRJnCxejFoDpYTOwzm5o21ZXMaNqtRlvS183XzGDUPRdVEzJNImcQokqYZ8BNTnk9DkKiuWxw75+DCCoZ26w== +webpack-cli@4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" + integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^2.1.0" - "@webpack-cli/info" "^2.0.1" - "@webpack-cli/serve" "^2.0.3" + "@webpack-cli/configtest" "^1.2.0" + "@webpack-cli/info" "^1.5.0" + "@webpack-cli/serve" "^1.7.0" colorette "^2.0.14" - commander "^10.0.1" + commander "^7.0.0" cross-spawn "^7.0.3" - envinfo "^7.7.3" fastest-levenshtein "^1.0.12" import-local "^3.0.2" - interpret "^3.1.1" - rechoir "^0.8.0" + interpret "^2.2.0" + rechoir "^0.7.0" webpack-merge "^5.7.3" webpack-dev-middleware@^5.3.1: diff --git a/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt b/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt index 5e33d9c22..d9906358b 100644 --- a/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt +++ b/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt @@ -1,3 +1,5 @@ +@file:OptIn(ExperimentalForeignApi::class, BetaInteropApi::class) + package com.arkivanov.sample.app import androidx.compose.foundation.layout.Box @@ -8,7 +10,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Application +import androidx.compose.ui.window.ComposeUIViewController import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.extensions.compose.jetbrains.PredictiveBackGestureIcon import com.arkivanov.decompose.extensions.compose.jetbrains.PredictiveBackGestureOverlay @@ -20,7 +22,8 @@ import com.arkivanov.essenty.lifecycle.stop import com.arkivanov.sample.shared.dynamicfeatures.dynamicfeature.DefaultFeatureInstaller import com.arkivanov.sample.shared.root.DefaultRootComponent import com.arkivanov.sample.shared.root.RootContent -import kotlinx.cinterop.ObjCObjectBase +import kotlinx.cinterop.BetaInteropApi +import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.autoreleasepool import kotlinx.cinterop.cstr import kotlinx.cinterop.memScoped @@ -46,12 +49,9 @@ fun main() { } } -class SkikoAppDelegate : UIResponder, UIApplicationDelegateProtocol { +class SkikoAppDelegate @OverrideInit constructor() : UIResponder(), UIApplicationDelegateProtocol { companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta - @ObjCObjectBase.OverrideInit - constructor() : super() - private val backDispatcher = BackDispatcher() private val lifecycle = LifecycleRegistry() @@ -70,13 +70,7 @@ class SkikoAppDelegate : UIResponder, UIApplicationDelegateProtocol { override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map?): Boolean { window = UIWindow(frame = UIScreen.mainScreen.bounds) -// backDispatcher.register( -// BackCallback { -// window!!.hidden = true -// } -// ) - - window!!.rootViewController = Application("Minesweeper") { + window!!.rootViewController = ComposeUIViewController { Column { // To skip upper part of screen. Box(modifier = Modifier.height(64.dp)) From 9cb8c2e9be2af37db549bb89b0e266c3ac4808b6 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 17 Aug 2023 15:56:21 +0100 Subject: [PATCH 11/89] Updated Compose for Web sample for Kotlin 1.9.0 --- sample/app-js-compose/build.gradle.kts | 2 ++ settings.gradle.kts | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sample/app-js-compose/build.gradle.kts b/sample/app-js-compose/build.gradle.kts index 5771bf9bd..d01c56a48 100644 --- a/sample/app-js-compose/build.gradle.kts +++ b/sample/app-js-compose/build.gradle.kts @@ -27,6 +27,8 @@ kotlin { implementation(compose.runtime) implementation(compose.foundation) implementation(compose.material) + implementation(project.dependencies.enforcedPlatform(deps.jetbrains.kotlinWrappers.kotlinWrappersBom.get())) + implementation("org.jetbrains.kotlin-wrappers:kotlin-browser") } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index f4a99455c..f616fb230 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -38,7 +38,10 @@ if (!startParameter.projectProperties.containsKey("check_publication")) { include(":sample:app-android") include(":sample:app-desktop") include(":sample:app-darwin-compose") - include(":sample:app-js-compose") + + // Disabled until Kotlin 1.9.10, see https://youtrack.jetbrains.com/issue/KT-60852 +// include(":sample:app-js-compose") + include(":sample:app-js") } else { include(":tools:check-publication") From 42e23f0c442edbd7861684811653d5165e213ab6 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 17 Aug 2023 18:29:22 +0100 Subject: [PATCH 12/89] Removed cacheKind workaround --- gradle.properties | 7 ------- 1 file changed, 7 deletions(-) diff --git a/gradle.properties b/gradle.properties index 2b8f0ade5..040c63887 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,10 +13,3 @@ org.jetbrains.compose.experimental.jscanvas.enabled=true # For compatibility with Kotlin 1.9.0 android.experimental.lint.version=8.1.0 - -# Workaround for a build failure on CI: -# e: Module "org.jetbrains.compose.runtime:runtime-saveable (org.jetbrains.compose.runtime:runtime-saveable-uikitx64)" -# has a reference to symbol androidx.compose.runtime/remember|-2215966373931868872[0]. Neither the module itself nor -# its dependencies contain such declaration. -# See: https://github.com/JetBrains/compose-jb/issues/2046 -kotlin.native.cacheKind=none From 0b753963d421e08f59bf94180b219279084f76c9 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 19 Aug 2023 11:58:49 +0100 Subject: [PATCH 13/89] Bumped version to 2.1.0-compose-experimental-alpha-07 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 1e59f4786..4f07fdaef 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.1.0-compose-experimental-alpha-06" +decompose = "2.1.0-compose-experimental-alpha-07" kotlin = "1.9.0" essenty = "1.2.0-alpha-06" parcelizeDarwin = "0.2.0" From ec8c28ae7833dc29ed63b77a00b239569fd23188 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 9 Sep 2023 09:29:49 +0100 Subject: [PATCH 14/89] Bumped version to 2.1.0-compose-experimental-beta-01 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 2dc2c2671..dab9a9926 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.1.0-compose-experimental-alpha-08" +decompose = "2.1.0-compose-experimental-beta-01" kotlin = "1.9.0" essenty = "1.2.0-beta-01" parcelizeDarwin = "0.2.1" From 645a43571c0b731fe0e32f8216aad07bd3291d7c Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Wed, 20 Sep 2023 23:01:05 +0100 Subject: [PATCH 15/89] Updated yarn.lock --- kotlin-js-store/yarn.lock | 259 ++------------------------------------ 1 file changed, 12 insertions(+), 247 deletions(-) diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index 2bcae5287..5007af391 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -9,14 +9,6 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/code-frame@^7.10.4": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" - integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== - dependencies: - "@babel/highlight" "^7.22.13" - chalk "^2.4.2" - "@babel/generator@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" @@ -77,11 +69,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-identifier@^7.22.5": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044" - integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ== - "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -91,15 +78,6 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.22.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.13.tgz#9cda839e5d3be9ca9e8c26b6dd69e7548f0cbf16" - integrity sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ== - dependencies: - "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.4.2" - js-tokens "^4.0.0" - "@babel/parser@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" @@ -542,48 +520,6 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== -"@rollup/plugin-commonjs@^21.0.1": - version "21.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.1.0.tgz#45576d7b47609af2db87f55a6d4b46e44fc3a553" - integrity sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA== - dependencies: - "@rollup/pluginutils" "^3.1.0" - commondir "^1.0.1" - estree-walker "^2.0.1" - glob "^7.1.6" - is-reference "^1.2.1" - magic-string "^0.25.7" - resolve "^1.17.0" - -"@rollup/plugin-node-resolve@^13.1.3": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz#da1c5c5ce8316cef96a2f823d111c1e4e498801c" - integrity sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw== - dependencies: - "@rollup/pluginutils" "^3.1.0" - "@types/resolve" "1.17.1" - deepmerge "^4.2.2" - is-builtin-module "^3.1.0" - is-module "^1.0.0" - resolve "^1.19.0" - -"@rollup/plugin-typescript@^8.3.0": - version "8.5.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz#7ea11599a15b0a30fa7ea69ce3b791d41b862515" - integrity sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ== - dependencies: - "@rollup/pluginutils" "^3.1.0" - resolve "^1.17.0" - -"@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" - integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== - dependencies: - "@types/estree" "0.0.39" - estree-walker "^1.0.1" - picomatch "^2.2.2" - "@socket.io/component-emitter@~3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" @@ -650,11 +586,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== -"@types/estree@0.0.39": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" - integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== - "@types/estree@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" @@ -706,11 +637,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.8.tgz#50d680c8a8a78fe30abe6906453b21ad8ab0ad7b" integrity sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg== -"@types/node@^12.12.14": - version "12.20.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" - integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== - "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -768,13 +694,6 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/resolve@1.17.1": - version "1.17.1" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" - integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== - dependencies: - "@types/node" "*" - "@types/retry@0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -1089,11 +1008,6 @@ array-flatten@^2.1.2: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - babel-plugin-macros@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" @@ -1226,11 +1140,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -builtin-modules@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" - integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== - bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -1274,7 +1183,7 @@ caniuse-lite@^1.0.30001286: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz#d753bf6444ed401eb503cbbe17aa3e1451b5a68c" integrity sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw== -chalk@^2.0.0, chalk@^2.4.2: +chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1398,11 +1307,6 @@ commander@^7.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== - compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -1575,16 +1479,6 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -decode-uri-component@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== - -deepmerge@^4.2.2: - version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" - integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== - default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" @@ -1773,16 +1667,6 @@ estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -estree-walker@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" - integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== - -estree-walker@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" - integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== - etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -2038,18 +1922,6 @@ glob@7.2.0, glob@^7.1.3, glob@^7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.6: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -2279,13 +2151,6 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-builtin-module@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" - integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== - dependencies: - builtin-modules "^3.3.0" - is-core-module@^2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" @@ -2322,11 +2187,6 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" - integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -2349,13 +2209,6 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-reference@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" - integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== - dependencies: - "@types/estree" "*" - is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -2393,15 +2246,6 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -jest-worker@^26.2.1: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -2578,13 +2422,6 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -magic-string@^0.25.7: - version "0.25.9" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" - integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== - dependencies: - sourcemap-codec "^1.4.8" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -2678,13 +2515,6 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - minimist@^1.2.3: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -2949,7 +2779,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.0, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.0, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -3155,15 +2985,6 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.17.0, resolve@^1.9.0: - version "1.22.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" - integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - resolve@^1.19.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -3173,6 +2994,15 @@ resolve@^1.19.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.9.0: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + retry@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" @@ -3195,31 +3025,6 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rollup-plugin-sourcemaps@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz#bf93913ffe056e414419607f1d02780d7ece84ed" - integrity sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw== - dependencies: - "@rollup/pluginutils" "^3.0.9" - source-map-resolve "^0.6.0" - -rollup-plugin-terser@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" - integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ== - dependencies: - "@babel/code-frame" "^7.10.4" - jest-worker "^26.2.1" - serialize-javascript "^4.0.0" - terser "^5.0.0" - -rollup@^2.68.0: - version "2.79.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" - integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== - optionalDependencies: - fsevents "~2.3.2" - safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -3308,13 +3113,6 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== - dependencies: - randombytes "^2.1.0" - serialize-javascript@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" @@ -3446,14 +3244,6 @@ source-map-loader@4.0.1: iconv-lite "^0.6.3" source-map-js "^1.0.2" -source-map-resolve@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" - integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -3472,11 +3262,6 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sourcemap-codec@^1.4.8: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" @@ -3594,7 +3379,7 @@ supports-color@^5.3.0, supports-color@^5.5.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -3622,16 +3407,6 @@ terser-webpack-plugin@^5.3.7: serialize-javascript "^6.0.1" terser "^5.16.8" -terser@^5.0.0: - version "5.19.4" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.4.tgz#941426fa482bf9b40a0308ab2b3cd0cf7c775ebd" - integrity sha512-6p1DjHeuluwxDXcuT9VR8p64klWJKo1ILiy19s6C9+0Bh2+NWTX6nD9EPppiER4ICkHDVB1RkVpin/YW2nQn/g== - dependencies: - "@jridgewell/source-map" "^0.3.3" - acorn "^8.8.2" - commander "^2.20.0" - source-map-support "~0.5.20" - terser@^5.16.8: version "5.19.2" resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.2.tgz#bdb8017a9a4a8de4663a7983f45c506534f9234e" @@ -3671,11 +3446,6 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -tslib@^2.3.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -3689,11 +3459,6 @@ typescript@5.0.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== -typescript@^3.7.2: - version "3.9.10" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" - integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== - ua-parser-js@^0.7.30: version "0.7.31" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" From 21ead7b313d4c159c7ddd0431709b63f4a5349a2 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 24 Sep 2023 09:35:38 +0100 Subject: [PATCH 16/89] Bumped version to 2.2.0-compose-experimental-alpha01 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 1bde13c43..23af9f467 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.1.0-compose-experimental" +decompose = "2.2.0-compose-experimental-alpha01" kotlin = "1.9.10" essenty = "1.3.0-alpha01" parcelizeDarwin = "0.2.2" From cfcc5204cb1bc5ee60b7293a52cc2aecfa57dd35 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 1 Oct 2023 10:03:38 +0100 Subject: [PATCH 17/89] Enabled app-js-compose --- settings.gradle.kts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index f616fb230..f4a99455c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -38,10 +38,7 @@ if (!startParameter.projectProperties.containsKey("check_publication")) { include(":sample:app-android") include(":sample:app-desktop") include(":sample:app-darwin-compose") - - // Disabled until Kotlin 1.9.10, see https://youtrack.jetbrains.com/issue/KT-60852 -// include(":sample:app-js-compose") - + include(":sample:app-js-compose") include(":sample:app-js") } else { include(":tools:check-publication") From dc4f361db8f8e2c095cec1925729a81343ff70bf Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 1 Oct 2023 10:35:25 +0100 Subject: [PATCH 18/89] Bumped version to 2.2.0-compose-experimental-alpha02 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 23af9f467..88dbdb296 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.2.0-compose-experimental-alpha01" +decompose = "2.2.0-compose-experimental-alpha02" kotlin = "1.9.10" essenty = "1.3.0-alpha01" parcelizeDarwin = "0.2.2" From 1813956c8a034ea40f6fd0b4cd58d9afff03622e Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 21 Oct 2023 15:07:53 +0100 Subject: [PATCH 19/89] Bumped version to 2.1.3-compose-experimental --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index acbf6986d..51f329a09 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.1.1-compose-experimental" +decompose = "2.1.3-compose-experimental" kotlin = "1.9.10" essenty = "1.2.0" parcelizeDarwin = "0.2.1" From d747458d9784854f1fe064e332743ef3fabbea80 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 5 Nov 2023 14:15:08 +0000 Subject: [PATCH 20/89] Bumped version to 2.2.0-compose-experimental-alpha04 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 17954f127..843de56b3 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.2.0-compose-experimental-alpha03" +decompose = "2.2.0-compose-experimental-alpha04" kotlin = "1.9.10" essenty = "1.3.0-alpha03" parcelizeDarwin = "0.2.2" From 402ffd0eb873b7f8101b6482e23fea86d3cccbe6 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 5 Nov 2023 14:16:26 +0000 Subject: [PATCH 21/89] Bumped version to 2.1.4-compose-experimental --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 51f329a09..9a19e1fb1 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.1.3-compose-experimental" +decompose = "2.1.4-compose-experimental" kotlin = "1.9.10" essenty = "1.2.0" parcelizeDarwin = "0.2.1" From 04b98a1de9fd20e9ea76f5b197faa28eba3559e7 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 16 Nov 2023 21:56:55 +0000 Subject: [PATCH 22/89] Updated samples --- .github/workflows/build.yml | 2 +- sample/app-darwin-compose/.gitignore | 1 - sample/app-darwin-compose/build.gradle.kts | 87 ---- .../kotlin/com/arkivanov/sample/app/Main.kt | 108 ----- sample/app-ios-compose/.gitignore | 18 + .../app-ios-compose.xcodeproj/project.pbxproj | 394 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../UserInterfaceState.xcuserstate | Bin 0 -> 27379 bytes .../xcschemes/xcschememanagement.plist | 14 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 63 +++ .../Assets.xcassets/Contents.json | 6 + .../Preview Assets.xcassets/Contents.json | 6 + .../app-ios-compose/RootView.swift | 16 + .../app_ios_compose.entitlements | 10 + .../app-ios-compose/iOSApp.swift | 47 +++ .../app-ios/app-ios.xcodeproj/project.pbxproj | 10 + sample/shared/compose/build.gradle.kts | 28 +- .../sample/shared/root/RootViewController.kt | 29 ++ sample/shared/shared/build.gradle.kts | 4 +- settings.gradle.kts | 1 - 22 files changed, 667 insertions(+), 203 deletions(-) delete mode 100644 sample/app-darwin-compose/.gitignore delete mode 100644 sample/app-darwin-compose/build.gradle.kts delete mode 100644 sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt create mode 100644 sample/app-ios-compose/.gitignore create mode 100644 sample/app-ios-compose/app-ios-compose.xcodeproj/project.pbxproj create mode 100644 sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 sample/app-ios-compose/app-ios-compose.xcodeproj/xcuserdata/arkivanov.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 sample/app-ios-compose/app-ios-compose/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 sample/app-ios-compose/app-ios-compose/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 sample/app-ios-compose/app-ios-compose/Assets.xcassets/Contents.json create mode 100644 sample/app-ios-compose/app-ios-compose/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 sample/app-ios-compose/app-ios-compose/RootView.swift create mode 100644 sample/app-ios-compose/app-ios-compose/app_ios_compose.entitlements create mode 100644 sample/app-ios-compose/app-ios-compose/iOSApp.swift create mode 100644 sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b9166cc0e..a4312f084 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: arguments: build -Dsplit_targets macos-build: name: Build on macOS - runs-on: macos-11 + runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v3 diff --git a/sample/app-darwin-compose/.gitignore b/sample/app-darwin-compose/.gitignore deleted file mode 100644 index 42afabfd2..000000000 --- a/sample/app-darwin-compose/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/sample/app-darwin-compose/build.gradle.kts b/sample/app-darwin-compose/build.gradle.kts deleted file mode 100644 index 661a93efa..000000000 --- a/sample/app-darwin-compose/build.gradle.kts +++ /dev/null @@ -1,87 +0,0 @@ -import com.arkivanov.gradle.bundle -import com.arkivanov.gradle.dependsOn -import com.arkivanov.gradle.setupMultiplatform -import com.arkivanov.gradle.setupSourceSets -import org.jetbrains.compose.experimental.dsl.IOSDevices -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget - -plugins { - id("kotlin-multiplatform") - id("org.jetbrains.compose") - id("com.arkivanov.gradle.setup") -} - -setupMultiplatform { - listOf(iosX64("uikitX64"), iosArm64("uikitArm64")).forEach { - it.binaries { - executable { - entryPoint = "com.arkivanov.sample.app.main" - freeCompilerArgs += listOf( - "-linker-option", "-framework", "-linker-option", "Metal", - "-linker-option", "-framework", "-linker-option", "CoreText", - "-linker-option", "-framework", "-linker-option", "CoreGraphics" - ) - } - } - } -} - -kotlin { - setupSourceSets { - val uikit by bundle() - - uikit dependsOn common - iosSet dependsOn uikit - - common.main.dependencies { - implementation(project(":decompose")) - implementation(project(":extensions-compose-jetbrains")) - implementation(project(":sample:shared:shared")) - implementation(project(":sample:shared:compose")) - implementation(compose.runtime) - implementation(compose.foundation) - implementation(compose.material) - } - } -} - -kotlin.targets.withType { - binaries.all { - binaryOptions["memoryModel"] = "experimental" - binaryOptions["freezing"] = "disabled" - } -} - -compose.experimental { - uikit.application { - bundleIdPrefix = "com.arkivanov.decompose.sample" - projectName = "DecomposeSample" - - deployConfigurations { - simulator("IPhone12Pro") { - // Usage: ./gradlew iosDeployIPhone12ProDebug - device = IOSDevices.IPHONE_12_PRO - } - - /* - * Usage: ./gradlew iosDeployDeviceDebug - * - * If your are getting "A valid provisioning profile for this executable was not found" error, - * see: https://developer.apple.com/forums/thread/128121?answerId=403323022#403323022 - */ - connectedDevice("Device") { - // Uncomment and fill with your Team ID - // teamId = "" - } - } - } -} - -kotlin { - targets.withType { - binaries.all { - // TODO: the current compose binary surprises LLVM, so disable checks for now. - freeCompilerArgs += "-Xdisable-phases=VerifyBitcode" - } - } -} diff --git a/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt b/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt deleted file mode 100644 index c41ee873f..000000000 --- a/sample/app-darwin-compose/src/uikitMain/kotlin/com/arkivanov/sample/app/Main.kt +++ /dev/null @@ -1,108 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class, BetaInteropApi::class) - -package com.arkivanov.sample.app - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.ComposeUIViewController -import com.arkivanov.decompose.DefaultComponentContext -import com.arkivanov.decompose.extensions.compose.jetbrains.PredictiveBackGestureIcon -import com.arkivanov.decompose.extensions.compose.jetbrains.PredictiveBackGestureOverlay -import com.arkivanov.essenty.backhandler.BackDispatcher -import com.arkivanov.essenty.lifecycle.LifecycleRegistry -import com.arkivanov.essenty.lifecycle.destroy -import com.arkivanov.essenty.lifecycle.resume -import com.arkivanov.essenty.lifecycle.stop -import com.arkivanov.sample.shared.dynamicfeatures.dynamicfeature.DefaultFeatureInstaller -import com.arkivanov.sample.shared.root.DefaultRootComponent -import com.arkivanov.sample.shared.root.RootContent -import kotlinx.cinterop.BetaInteropApi -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.autoreleasepool -import kotlinx.cinterop.cstr -import kotlinx.cinterop.memScoped -import kotlinx.cinterop.toCValues -import platform.Foundation.NSStringFromClass -import platform.UIKit.UIApplication -import platform.UIKit.UIApplicationDelegateProtocol -import platform.UIKit.UIApplicationDelegateProtocolMeta -import platform.UIKit.UIApplicationMain -import platform.UIKit.UIResponder -import platform.UIKit.UIResponderMeta -import platform.UIKit.UIScreen -import platform.UIKit.UIWindow - -fun main() { - val args = emptyArray() - memScoped { - val argc = args.size + 1 - val argv = (arrayOf("skikoApp") + args).map { it.cstr.ptr }.toCValues() - autoreleasepool { - UIApplicationMain(argc, argv, null, NSStringFromClass(SkikoAppDelegate)) - } - } -} - -class SkikoAppDelegate @OverrideInit constructor() : UIResponder(), UIApplicationDelegateProtocol { - companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta - - private val backDispatcher = BackDispatcher() - private val lifecycle = LifecycleRegistry() - - private val root = - DefaultRootComponent( - componentContext = DefaultComponentContext(lifecycle = lifecycle, backHandler = backDispatcher), - featureInstaller = DefaultFeatureInstaller, - ) - - private var _window: UIWindow? = null - override fun window() = _window - override fun setWindow(window: UIWindow?) { - _window = window - } - - override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map?): Boolean { - window = UIWindow(frame = UIScreen.mainScreen.bounds) - - window!!.rootViewController = ComposeUIViewController { - Column { - PredictiveBackGestureOverlay( - backDispatcher = backDispatcher, - backIcon = { progress, _ -> - PredictiveBackGestureIcon( - imageVector = Icons.Default.ArrowBack, - progress = progress, - ) - }, - modifier = Modifier.fillMaxSize(), - ) { - RootContent( - component = root, - modifier = Modifier.fillMaxSize(), - ) - } - } - } - window!!.makeKeyAndVisible() - return true - } - - override fun applicationDidBecomeActive(application: UIApplication) { - lifecycle.resume() - } - - override fun applicationWillResignActive(application: UIApplication) { - lifecycle.stop() - - } - - override fun applicationWillTerminate(application: UIApplication) { - lifecycle.destroy() - } -} diff --git a/sample/app-ios-compose/.gitignore b/sample/app-ios-compose/.gitignore new file mode 100644 index 000000000..97d5de824 --- /dev/null +++ b/sample/app-ios-compose/.gitignore @@ -0,0 +1,18 @@ +DerivedData/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ +*.moved-aside +*.xccheckout +*.xcscmblueprint +*.hmap +*.ipa +*.dSYM.zip +*.dSYM +Pods diff --git a/sample/app-ios-compose/app-ios-compose.xcodeproj/project.pbxproj b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.pbxproj new file mode 100644 index 000000000..004d5b419 --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.pbxproj @@ -0,0 +1,394 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 79DA70E72B06BD3700326407 /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79DA70E62B06BD3700326407 /* iOSApp.swift */; }; + 79DA70E92B06BD3700326407 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79DA70E82B06BD3700326407 /* RootView.swift */; }; + 79DA70EB2B06BD3800326407 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 79DA70EA2B06BD3800326407 /* Assets.xcassets */; }; + 79DA70EF2B06BD3800326407 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 79DA70EE2B06BD3800326407 /* Preview Assets.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 79DA70E32B06BD3700326407 /* app-ios-compose.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "app-ios-compose.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 79DA70E62B06BD3700326407 /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 79DA70E82B06BD3700326407 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = ""; }; + 79DA70EA2B06BD3800326407 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 79DA70EC2B06BD3800326407 /* app_ios_compose.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app_ios_compose.entitlements; sourceTree = ""; }; + 79DA70EE2B06BD3800326407 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 79DA70E02B06BD3700326407 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 79DA70DA2B06BD3700326407 = { + isa = PBXGroup; + children = ( + 79DA70E52B06BD3700326407 /* app-ios-compose */, + 79DA70E42B06BD3700326407 /* Products */, + ); + sourceTree = ""; + }; + 79DA70E42B06BD3700326407 /* Products */ = { + isa = PBXGroup; + children = ( + 79DA70E32B06BD3700326407 /* app-ios-compose.app */, + ); + name = Products; + sourceTree = ""; + }; + 79DA70E52B06BD3700326407 /* app-ios-compose */ = { + isa = PBXGroup; + children = ( + 79DA70E62B06BD3700326407 /* iOSApp.swift */, + 79DA70E82B06BD3700326407 /* RootView.swift */, + 79DA70EA2B06BD3800326407 /* Assets.xcassets */, + 79DA70EC2B06BD3800326407 /* app_ios_compose.entitlements */, + 79DA70ED2B06BD3800326407 /* Preview Content */, + ); + path = "app-ios-compose"; + sourceTree = ""; + }; + 79DA70ED2B06BD3800326407 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 79DA70EE2B06BD3800326407 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 79DA70E22B06BD3700326407 /* app-ios-compose */ = { + isa = PBXNativeTarget; + buildConfigurationList = 79DA70F22B06BD3800326407 /* Build configuration list for PBXNativeTarget "app-ios-compose" */; + buildPhases = ( + 79DA70F52B06BE4E00326407 /* ShellScript */, + 79DA70DF2B06BD3700326407 /* Sources */, + 79DA70E02B06BD3700326407 /* Frameworks */, + 79DA70E12B06BD3700326407 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "app-ios-compose"; + productName = "app-ios-compose"; + productReference = 79DA70E32B06BD3700326407 /* app-ios-compose.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 79DA70DB2B06BD3700326407 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + 79DA70E22B06BD3700326407 = { + CreatedOnToolsVersion = 14.3.1; + }; + }; + }; + buildConfigurationList = 79DA70DE2B06BD3700326407 /* Build configuration list for PBXProject "app-ios-compose" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 79DA70DA2B06BD3700326407; + productRefGroup = 79DA70E42B06BD3700326407 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 79DA70E22B06BD3700326407 /* app-ios-compose */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 79DA70E12B06BD3700326407 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 79DA70EF2B06BD3800326407 /* Preview Assets.xcassets in Resources */, + 79DA70EB2B06BD3800326407 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 79DA70F52B06BE4E00326407 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd \"$SRCROOT/../..\"\n./gradlew :sample:shared:compose:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 79DA70DF2B06BD3700326407 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 79DA70E92B06BD3700326407 /* RootView.swift in Sources */, + 79DA70E72B06BD3700326407 /* iOSApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 79DA70F02B06BD3800326407 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 79DA70F12B06BD3800326407 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 79DA70F32B06BD3800326407 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = "app-ios-compose/app_ios_compose.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"app-ios-compose/Preview Content\""; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../shared/compose/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)"; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + shared, + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.arkivanov.sample.app-ios-compose.app-ios-compose"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 79DA70F42B06BD3800326407 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = "app-ios-compose/app_ios_compose.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"app-ios-compose/Preview Content\""; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../shared/compose/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)"; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + shared, + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.arkivanov.sample.app-ios-compose.app-ios-compose"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 79DA70DE2B06BD3700326407 /* Build configuration list for PBXProject "app-ios-compose" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 79DA70F02B06BD3800326407 /* Debug */, + 79DA70F12B06BD3800326407 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 79DA70F22B06BD3800326407 /* Build configuration list for PBXNativeTarget "app-ios-compose" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 79DA70F32B06BD3800326407 /* Debug */, + 79DA70F42B06BD3800326407 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 79DA70DB2B06BD3700326407 /* Project object */; +} diff --git a/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..9d0313ba6682a7720b0bfd4bb1bc13452c5fe71f GIT binary patch literal 27379 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 literal 0 HcmV?d00001 diff --git a/sample/app-ios-compose/app-ios-compose.xcodeproj/xcuserdata/arkivanov.xcuserdatad/xcschemes/xcschememanagement.plist b/sample/app-ios-compose/app-ios-compose.xcodeproj/xcuserdata/arkivanov.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..2920fb6ac --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose.xcodeproj/xcuserdata/arkivanov.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + app-ios-compose.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/sample/app-ios-compose/app-ios-compose/Assets.xcassets/AccentColor.colorset/Contents.json b/sample/app-ios-compose/app-ios-compose/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sample/app-ios-compose/app-ios-compose/Assets.xcassets/AppIcon.appiconset/Contents.json b/sample/app-ios-compose/app-ios-compose/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..532cd729c --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,63 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sample/app-ios-compose/app-ios-compose/Assets.xcassets/Contents.json b/sample/app-ios-compose/app-ios-compose/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sample/app-ios-compose/app-ios-compose/Preview Content/Preview Assets.xcassets/Contents.json b/sample/app-ios-compose/app-ios-compose/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sample/app-ios-compose/app-ios-compose/RootView.swift b/sample/app-ios-compose/app-ios-compose/RootView.swift new file mode 100644 index 000000000..8cae907d0 --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose/RootView.swift @@ -0,0 +1,16 @@ +import SwiftUI +import Shared + +struct RootView: UIViewControllerRepresentable { + let root: RootComponent + let backDispatcher: BackDispatcher + + func makeUIViewController(context: Context) -> UIViewController { + let controller = RootViewControllerKt.rootViewController(root: root, backDispatcher: backDispatcher) + controller.overrideUserInterfaceStyle = .light + return controller + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + } +} diff --git a/sample/app-ios-compose/app-ios-compose/app_ios_compose.entitlements b/sample/app-ios-compose/app-ios-compose/app_ios_compose.entitlements new file mode 100644 index 000000000..f2ef3ae02 --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose/app_ios_compose.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + + diff --git a/sample/app-ios-compose/app-ios-compose/iOSApp.swift b/sample/app-ios-compose/app-ios-compose/iOSApp.swift new file mode 100644 index 000000000..07fe00db0 --- /dev/null +++ b/sample/app-ios-compose/app-ios-compose/iOSApp.swift @@ -0,0 +1,47 @@ +import SwiftUI +import Shared + +@main +struct iOSApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) + var appDelegate: AppDelegate + + var body: some Scene { + WindowGroup { + RootView(root: appDelegate.root, backDispatcher: appDelegate.backDispatcher) + .ignoresSafeArea(.all) + } + } +} + +class AppDelegate: NSObject, UIApplicationDelegate { + private var stateKeeper = StateKeeperDispatcherKt.StateKeeperDispatcher(savedState: nil) + var backDispatcher: BackDispatcher = BackDispatcherKt.BackDispatcher() + + lazy var root: RootComponent = DefaultRootComponent( + componentContext: DefaultComponentContext( + lifecycle: ApplicationLifecycle(), + stateKeeper: stateKeeper, + instanceKeeper: nil, + backHandler: backDispatcher + ), + featureInstaller: DefaultFeatureInstaller.shared, + deepLink: DefaultRootComponentDeepLinkNone.shared, + webHistoryController: nil + ) + + func application(_ application: UIApplication, shouldSaveSecureApplicationState coder: NSCoder) -> Bool { + CodingKt.encodeParcelable(coder, value: stateKeeper.save(), key: "savedState") + return true + } + + func application(_ application: UIApplication, shouldRestoreSecureApplicationState coder: NSCoder) -> Bool { + do { + let savedState = try CodingKt.decodeParcelable(coder, key: "savedState") as! ParcelableParcelableContainer + stateKeeper = StateKeeperDispatcherKt.StateKeeperDispatcher(savedState: savedState) + return true + } catch { + return false + } + } +} diff --git a/sample/app-ios/app-ios.xcodeproj/project.pbxproj b/sample/app-ios/app-ios.xcodeproj/project.pbxproj index 73c92b630..87829c77a 100644 --- a/sample/app-ios/app-ios.xcodeproj/project.pbxproj +++ b/sample/app-ios/app-ios.xcodeproj/project.pbxproj @@ -359,6 +359,11 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + shared, + ); PRODUCT_BUNDLE_IDENTIFIER = "com.arkivanov.sample.app-ios"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -388,6 +393,11 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + shared, + ); PRODUCT_BUNDLE_IDENTIFIER = "com.arkivanov.sample.app-ios"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/sample/shared/compose/build.gradle.kts b/sample/shared/compose/build.gradle.kts index 48ee55122..6ee286fa7 100644 --- a/sample/shared/compose/build.gradle.kts +++ b/sample/shared/compose/build.gradle.kts @@ -3,6 +3,8 @@ import com.arkivanov.gradle.dependsOn import com.arkivanov.gradle.iosCompat import com.arkivanov.gradle.setupMultiplatform import com.arkivanov.gradle.setupSourceSets +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.konan.target.Family plugins { id("kotlin-multiplatform") @@ -23,6 +25,28 @@ android { } kotlin { + targets + .filterIsInstance() + .filter { it.konanTarget.family == Family.IOS } + .forEach { + it.binaries { + framework { + baseName = "Shared" // Used by app-ios-compose + export(project(":decompose")) + export(project(":sample:shared:shared")) + + // Optional, only if you need Predictive Back Gesture on Darwin (Apple) targets + export(deps.essenty.backHandler) + + // Optional, only if you need state preservation on Darwin (Apple) targets + export(deps.essenty.stateKeeper) + + // Optional, only if you need state preservation on Darwin (Apple) targets + export(deps.parcelizeDarwin.runtime) + } + } + } + setupSourceSets { val jvm by bundle() val ios by bundle() @@ -31,9 +55,9 @@ kotlin { iosSet dependsOn ios common.main.dependencies { - implementation(project(":decompose")) + api(project(":decompose")) implementation(project(":extensions-compose-jetbrains")) - implementation(project(":sample:shared:shared")) + api(project(":sample:shared:shared")) implementation(project(":sample:shared:dynamic-features:api")) implementation(project(":sample:shared:dynamic-features:compose-api")) implementation(compose.runtime) diff --git a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt new file mode 100644 index 000000000..d5e9e62a3 --- /dev/null +++ b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt @@ -0,0 +1,29 @@ +package com.arkivanov.sample.shared.root + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.ui.Modifier +import androidx.compose.ui.window.ComposeUIViewController +import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.decompose.extensions.compose.jetbrains.PredictiveBackGestureIcon +import com.arkivanov.decompose.extensions.compose.jetbrains.PredictiveBackGestureOverlay +import com.arkivanov.essenty.backhandler.BackDispatcher +import platform.UIKit.UIViewController + +@OptIn(ExperimentalDecomposeApi::class) +fun rootViewController(root: RootComponent, backDispatcher: BackDispatcher): UIViewController = + ComposeUIViewController { + PredictiveBackGestureOverlay( + backDispatcher = backDispatcher, + backIcon = { progress, _ -> + PredictiveBackGestureIcon( + imageVector = Icons.Default.ArrowBack, + progress = progress, + ) + }, + modifier = Modifier.fillMaxSize(), + ) { + RootContent(component = root, modifier = Modifier.fillMaxSize()) + } + } diff --git a/sample/shared/shared/build.gradle.kts b/sample/shared/shared/build.gradle.kts index 8d432938e..0a2bfedd0 100644 --- a/sample/shared/shared/build.gradle.kts +++ b/sample/shared/shared/build.gradle.kts @@ -32,9 +32,8 @@ kotlin { .forEach { it.binaries { framework { - baseName = "Shared" + baseName = "Shared" // Used by app-ios export(project(":decompose")) - export(deps.essenty.lifecycle) // Optional, only if you need state preservation on Darwin (Apple) targets export(deps.essenty.stateKeeper) @@ -62,7 +61,6 @@ kotlin { implementation(deps.reaktive.reaktive) } - common.test.dependencies { implementation(deps.reaktive.reaktiveTesting) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 7956392df..f3cbe6116 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -37,7 +37,6 @@ if (!startParameter.projectProperties.containsKey("check_publication")) { include(":sample:shared:dynamic-features:feature2Impl") include(":sample:app-android") include(":sample:app-desktop") - include(":sample:app-darwin-compose") include(":sample:app-js-compose") include(":sample:app-js") } else { From 10d70e186691e9f57b98f5ea3e726fe46a8b5656 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 17 Nov 2023 19:20:01 +0000 Subject: [PATCH 23/89] Bumped version to 2.2.0-compose-experimental-alpha05 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index a9e3e7c96..31df3c574 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.2.0-compose-experimental-alpha04" +decompose = "2.2.0-compose-experimental-alpha05" kotlin = "1.9.20" essenty = "1.3.0-alpha04" parcelizeDarwin = "0.2.3" From e793098a05ac6e77c602eca1208a4265a3fa2809 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 25 Nov 2023 18:50:35 +0000 Subject: [PATCH 24/89] Bumped version to 2.2.0-compose-experimental-beta02 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 8ea9bacf7..e437eceff 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.2.0-compose-experimental-beta01" +decompose = "2.2.0-compose-experimental-beta02" kotlin = "1.9.20" essenty = "1.3.0-beta01" parcelizeDarwin = "0.2.3" From cac3718398c45e0d2b8f973597172e6466a20cbd Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 30 Nov 2023 23:46:05 +0000 Subject: [PATCH 25/89] Updated gradle-setup-plugin plugin, disabled debug artifact publications for Android --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index c443475b8..5299ac2fa 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,7 @@ pluginManagement { resolutionStrategy { eachPlugin { if (requested.id.toString() == "com.arkivanov.gradle.setup") { - useModule("com.github.arkivanov:gradle-setup-plugin:655aedff78") + useModule("com.github.arkivanov:gradle-setup-plugin:4a116614b3") } } } From ca5571b19d8ea8542f639ad88b43bccc323900d9 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 3 Dec 2023 17:40:13 +0000 Subject: [PATCH 26/89] Removed unused deprecated code --- decompose/api/android/decompose.api | 53 --- decompose/api/jvm/decompose.api | 53 --- .../router/children/ChildrenFactory.kt | 2 +- .../decompose/router/overlay/ChildOverlay.kt | 17 - .../router/overlay/ChildOverlayFactory.kt | 142 -------- .../overlay/DefaultOverlayNavigation.kt | 24 -- .../router/overlay/OverlayNavigation.kt | 21 -- .../router/overlay/OverlayNavigationSource.kt | 23 -- .../router/overlay/OverlayNavigator.kt | 37 -- .../router/overlay/OverlayNavigatorExt.kt | 32 -- .../decompose/router/overlay/ValueExt.kt | 8 - .../overlay/ChildOverlayIntegrationTest.kt | 316 ------------------ .../android/extensions-compose-jetbrains.api | 9 - .../api/jvm/extensions-compose-jetbrains.api | 9 - .../jetbrains/stack/animation/Slide.kt | 12 - 15 files changed, 1 insertion(+), 757 deletions(-) delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlay.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/DefaultOverlayNavigation.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigation.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigator.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigatorExt.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ValueExt.kt delete mode 100644 decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayIntegrationTest.kt diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 23172c210..1ead9a197 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -148,59 +148,6 @@ public final class com/arkivanov/decompose/router/children/SimpleNavigation : co public fun unsubscribe (Lkotlin/jvm/functions/Function1;)V } -public final class com/arkivanov/decompose/router/overlay/ChildOverlay { - public fun ()V - public fun (Lcom/arkivanov/decompose/Child$Created;)V - public synthetic fun (Lcom/arkivanov/decompose/Child$Created;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Lcom/arkivanov/decompose/Child$Created; - public final fun copy (Lcom/arkivanov/decompose/Child$Created;)Lcom/arkivanov/decompose/router/overlay/ChildOverlay; - public static synthetic fun copy$default (Lcom/arkivanov/decompose/router/overlay/ChildOverlay;Lcom/arkivanov/decompose/Child$Created;ILjava/lang/Object;)Lcom/arkivanov/decompose/router/overlay/ChildOverlay; - public fun equals (Ljava/lang/Object;)Z - public final fun getOverlay ()Lcom/arkivanov/decompose/Child$Created; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/arkivanov/decompose/router/overlay/ChildOverlayFactoryKt { - public static final fun childOverlay (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/overlay/OverlayNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childOverlay (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/overlay/OverlayNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childOverlay$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/overlay/OverlayNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childOverlay$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/overlay/OverlayNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; -} - -public abstract interface class com/arkivanov/decompose/router/overlay/OverlayNavigation : com/arkivanov/decompose/router/overlay/OverlayNavigationSource, com/arkivanov/decompose/router/overlay/OverlayNavigator { -} - -public final class com/arkivanov/decompose/router/overlay/OverlayNavigationKt { - public static final fun OverlayNavigation ()Lcom/arkivanov/decompose/router/overlay/OverlayNavigation; -} - -public abstract interface class com/arkivanov/decompose/router/overlay/OverlayNavigationSource : com/arkivanov/decompose/router/children/NavigationSource { -} - -public final class com/arkivanov/decompose/router/overlay/OverlayNavigationSource$Event { - public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V - public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getOnComplete ()Lkotlin/jvm/functions/Function2; - public final fun getTransformer ()Lkotlin/jvm/functions/Function1; -} - -public abstract interface class com/arkivanov/decompose/router/overlay/OverlayNavigator { - public abstract fun navigate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V -} - -public final class com/arkivanov/decompose/router/overlay/OverlayNavigatorExtKt { - public static final fun activate (Lcom/arkivanov/decompose/router/overlay/OverlayNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V - public static synthetic fun activate$default (Lcom/arkivanov/decompose/router/overlay/OverlayNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V - public static final fun dismiss (Lcom/arkivanov/decompose/router/overlay/OverlayNavigator;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun dismiss$default (Lcom/arkivanov/decompose/router/overlay/OverlayNavigator;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V - public static final fun navigate (Lcom/arkivanov/decompose/router/overlay/OverlayNavigator;Lkotlin/jvm/functions/Function1;)V -} - -public final class com/arkivanov/decompose/router/overlay/ValueExtKt { - public static final fun getOverlay (Lcom/arkivanov/decompose/value/Value;)Lcom/arkivanov/decompose/Child$Created; -} - public final class com/arkivanov/decompose/router/pages/ChildPages { public fun ()V public fun (Ljava/util/List;I)V diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api index 38396ab4d..a0a1cebaa 100644 --- a/decompose/api/jvm/decompose.api +++ b/decompose/api/jvm/decompose.api @@ -118,59 +118,6 @@ public final class com/arkivanov/decompose/router/children/SimpleNavigation : co public fun unsubscribe (Lkotlin/jvm/functions/Function1;)V } -public final class com/arkivanov/decompose/router/overlay/ChildOverlay { - public fun ()V - public fun (Lcom/arkivanov/decompose/Child$Created;)V - public synthetic fun (Lcom/arkivanov/decompose/Child$Created;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Lcom/arkivanov/decompose/Child$Created; - public final fun copy (Lcom/arkivanov/decompose/Child$Created;)Lcom/arkivanov/decompose/router/overlay/ChildOverlay; - public static synthetic fun copy$default (Lcom/arkivanov/decompose/router/overlay/ChildOverlay;Lcom/arkivanov/decompose/Child$Created;ILjava/lang/Object;)Lcom/arkivanov/decompose/router/overlay/ChildOverlay; - public fun equals (Ljava/lang/Object;)Z - public final fun getOverlay ()Lcom/arkivanov/decompose/Child$Created; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/arkivanov/decompose/router/overlay/ChildOverlayFactoryKt { - public static final fun childOverlay (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/overlay/OverlayNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childOverlay (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/overlay/OverlayNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childOverlay$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/overlay/OverlayNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childOverlay$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/overlay/OverlayNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; -} - -public abstract interface class com/arkivanov/decompose/router/overlay/OverlayNavigation : com/arkivanov/decompose/router/overlay/OverlayNavigationSource, com/arkivanov/decompose/router/overlay/OverlayNavigator { -} - -public final class com/arkivanov/decompose/router/overlay/OverlayNavigationKt { - public static final fun OverlayNavigation ()Lcom/arkivanov/decompose/router/overlay/OverlayNavigation; -} - -public abstract interface class com/arkivanov/decompose/router/overlay/OverlayNavigationSource : com/arkivanov/decompose/router/children/NavigationSource { -} - -public final class com/arkivanov/decompose/router/overlay/OverlayNavigationSource$Event { - public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V - public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getOnComplete ()Lkotlin/jvm/functions/Function2; - public final fun getTransformer ()Lkotlin/jvm/functions/Function1; -} - -public abstract interface class com/arkivanov/decompose/router/overlay/OverlayNavigator { - public abstract fun navigate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V -} - -public final class com/arkivanov/decompose/router/overlay/OverlayNavigatorExtKt { - public static final fun activate (Lcom/arkivanov/decompose/router/overlay/OverlayNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V - public static synthetic fun activate$default (Lcom/arkivanov/decompose/router/overlay/OverlayNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V - public static final fun dismiss (Lcom/arkivanov/decompose/router/overlay/OverlayNavigator;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun dismiss$default (Lcom/arkivanov/decompose/router/overlay/OverlayNavigator;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V - public static final fun navigate (Lcom/arkivanov/decompose/router/overlay/OverlayNavigator;Lkotlin/jvm/functions/Function1;)V -} - -public final class com/arkivanov/decompose/router/overlay/ValueExtKt { - public static final fun getOverlay (Lcom/arkivanov/decompose/value/Value;)Lcom/arkivanov/decompose/Child$Created; -} - public final class com/arkivanov/decompose/router/pages/ChildPages { public fun ()V public fun (Ljava/util/List;I)V diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt index 9558edb81..4996057fb 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt @@ -65,7 +65,7 @@ fun ComponentContext.children( * Initialised and manages a generic list of components. This is an API for custom navigation models. * Please consider the existing navigation models first: * [Child Stack][com.arkivanov.decompose.router.stack.childStack], - * [Child Slot][com.arkivanov.decompose.router.overlay.childSlot]. + * [Child Slot][com.arkivanov.decompose.router.slot.childSlot]. * * The API is based around [NavState] and [ChildNavState] interfaces that should be implemented by * clients. [NavState] represents a persistent state of the navigation. It also holds a navigation diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlay.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlay.kt deleted file mode 100644 index 226afef8c..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlay.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.arkivanov.decompose.router.overlay - -import com.arkivanov.decompose.Child - -/** - * A state holder for `Child Overlay`. - */ -@Deprecated( - message = "Please use Child Slot API", - replaceWith = ReplaceWith( - expression = "ChildSlot", - "com.arkivanov.decompose.router.slot.ChildSlot", - ), -) -data class ChildOverlay( - val overlay: Child.Created? = null, -) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt deleted file mode 100644 index 469936ec2..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt +++ /dev/null @@ -1,142 +0,0 @@ -package com.arkivanov.decompose.router.overlay - -import com.arkivanov.decompose.Child -import com.arkivanov.decompose.ComponentContext -import com.arkivanov.decompose.router.children.ChildNavState.Status -import com.arkivanov.decompose.router.children.NavState -import com.arkivanov.decompose.router.children.SimpleChildNavState -import com.arkivanov.decompose.router.children.children -import com.arkivanov.decompose.value.Value -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.ParcelableContainer -import kotlin.reflect.KClass - -/** - * Initializes and manages component overlay. An overlay component can be either active or dismissed (destroyed). - * - * @param source a source of navigation events. - * @param configurationClass a [KClass] of the component configurations. - * @param key a key of the overlay, must be unique within the parent (hosting) component. - * @param initialConfiguration a component configuration that should be shown if there is - * no saved state, return `null` to show nothing. - * @param persistent determines whether the navigation state should pre preserved or not, - * default is `true`. - * @param handleBackButton determines whether the overlay should be automatically dismissed - * on back button press or not, default is `false`. - * @param childFactory a factory function that creates new child instances. - * @return an observable [Value] of [ChildOverlay]. - */ -@Deprecated( - message = "Please use Child Slot API", - replaceWith = ReplaceWith( - expression = "childSlot", - "com.arkivanov.decompose.router.slot.childSlot", - ), -) -fun ComponentContext.childOverlay( - source: OverlayNavigationSource, - configurationClass: KClass, - key: String = "DefaultChildOverlay", - initialConfiguration: () -> C? = { null }, - persistent: Boolean = true, - handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, -): Value> = - childOverlay( - source = source, - saveConfiguration = { if (persistent) ParcelableContainer(it) else null }, - restoreConfiguration = { it.consume(configurationClass) }, - key = key, - initialConfiguration = initialConfiguration, - handleBackButton = handleBackButton, - childFactory = childFactory, - ) - -/** - * Initializes and manages component overlay. An overlay component can be either active or dismissed (destroyed). - * - * @param source a source of navigation events. - * @param key a key of the overlay, must be unique within the parent (hosting) component. - * @param saveConfiguration a function that saves the provided configuration into [ParcelableContainer]. - * @param restoreConfiguration a function that restores the configuration from the provided [ParcelableContainer]. - * @param initialConfiguration a component configuration that should be shown if there is - * no saved state, return `null` to show nothing. - * @param handleBackButton determines whether the overlay should be automatically dismissed - * on back button press or not, default is `false`. - * @param childFactory a factory function that creates new child instances. - * @return an observable [Value] of [ChildOverlay]. - */ -@Deprecated( - message = "Please use Child Slot API", - replaceWith = ReplaceWith( - expression = "childSlot", - "com.arkivanov.decompose.router.slot.childSlot", - ), -) -fun ComponentContext.childOverlay( - source: OverlayNavigationSource, - saveConfiguration: (C?) -> ParcelableContainer?, - restoreConfiguration: (ParcelableContainer) -> C?, - key: String = "DefaultChildOverlay", - initialConfiguration: () -> C? = { null }, - handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, -): Value> = - children( - source = source, - key = key, - initialState = { OverlayNavState(configuration = initialConfiguration()) }, - saveState = { saveConfiguration(it.configuration) }, - restoreState = { OverlayNavState(restoreConfiguration(it)) }, - navTransformer = { state, event -> OverlayNavState(configuration = event.transformer(state.configuration)) }, - stateMapper = { _, children -> ChildOverlay(overlay = children.firstOrNull() as? Child.Created?) }, - onEventComplete = { event, newState, oldState -> event.onComplete(newState.configuration, oldState.configuration) }, - backTransformer = { state -> - if (handleBackButton && (state.configuration != null)) { - { OverlayNavState(configuration = null) } - } else { - null - } - }, - childFactory = childFactory, - ) - -/** - * A convenience extension function for [ComponentContext.childOverlay]. - */ -@Deprecated( - message = "Please use Child Slot API", - replaceWith = ReplaceWith( - expression = "this.childSlot(source, key, initialConfiguration, persistent, handleBackButton, childFactory)", - "com.arkivanov.decompose.router.slot.childSlot", - ), -) -inline fun ComponentContext.childOverlay( - source: OverlayNavigationSource, - key: String = "DefaultChildOverlay", - noinline initialConfiguration: () -> C? = { null }, - persistent: Boolean = true, - handleBackButton: Boolean = false, - noinline childFactory: (configuration: C, ComponentContext) -> T, -): Value> = - childOverlay( - source = source, - key = key, - configurationClass = C::class, - initialConfiguration = initialConfiguration, - persistent = persistent, - handleBackButton = handleBackButton, - childFactory = childFactory, - ) - -private data class OverlayNavState( - val configuration: C?, -) : NavState { - - override val children: List> = - if (configuration == null) { - emptyList() - } else { - listOf(SimpleChildNavState(configuration = configuration, status = Status.ACTIVE)) - } -} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/DefaultOverlayNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/DefaultOverlayNavigation.kt deleted file mode 100644 index abd703598..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/DefaultOverlayNavigation.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.arkivanov.decompose.router.overlay - -import com.arkivanov.decompose.Relay -import com.arkivanov.decompose.router.overlay.OverlayNavigationSource.Event - -internal class DefaultOverlayNavigation : OverlayNavigation { - - private val relay = Relay>(isMainThreadCheckEnabled = true) - - override fun navigate( - transformer: (configuration: C?) -> C?, - onComplete: (newConfiguration: C?, oldConfiguration: C?) -> Unit, - ) { - relay.accept(Event(transformer, onComplete)) - } - - override fun subscribe(observer: (Event) -> Unit) { - relay.subscribe(observer) - } - - override fun unsubscribe(observer: (Event) -> Unit) { - relay.unsubscribe(observer) - } -} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigation.kt deleted file mode 100644 index 9af808e53..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigation.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.arkivanov.decompose.router.overlay - -/** - * Represents [OverlayNavigator] and [OverlayNavigationSource] at the same time. - */ -interface OverlayNavigation : OverlayNavigator, OverlayNavigationSource - -/** - * Returns a default implementation of [OverlayNavigation]. - * Broadcasts navigation events to all subscribed observers. - */ -@Deprecated( - message = "Please use Child Slot API", - replaceWith = ReplaceWith( - expression = "SlotNavigation()", - "com.arkivanov.decompose.router.slot.SlotNavigation", - ), -) -@Suppress("FunctionName") // Factory function -fun OverlayNavigation(): OverlayNavigation = - DefaultOverlayNavigation() diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt deleted file mode 100644 index d1d285773..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.arkivanov.decompose.router.overlay - -import com.arkivanov.decompose.router.children.NavigationSource - -/** - * Represents a source of navigation events for `Child Overlay`. - * - * @see OverlayNavigator - */ -@Deprecated( - message = "Please use Child Slot API", - replaceWith = ReplaceWith( - expression = "SlotNavigationSource", - "com.arkivanov.decompose.router.slot.SlotNavigationSource", - ), -) -interface OverlayNavigationSource : NavigationSource> { - - class Event( - val transformer: (configuration: C?) -> C?, - val onComplete: (newConfiguration: C?, oldConfiguration: C?) -> Unit = { _, _ -> }, - ) -} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigator.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigator.kt deleted file mode 100644 index eee101729..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigator.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.arkivanov.decompose.router.overlay - -@Deprecated( - message = "Please use Child Slot API", - replaceWith = ReplaceWith( - expression = "OverlayNavigator", - "com.arkivanov.decompose.router.slot.OverlayNavigator", - ), -) -interface OverlayNavigator { - - /** - * Transforms the current configuration into a new one. Configuration `null` means that the - * component is not shown. - * - * During the navigation process, the `Child Overlay` compares the new configuration with - * the previous one. The `Child Overlay` ensures that the current component is resumed, and a - * dismissed component is destroyed. - * - * The `Child Overlay` usually performs the navigation synchronously, which means that by the time - * the `navigate` method returns, the navigation is finished and all component lifecycles are - * moved into required states. However, the navigation is performed asynchronously in case of - * recursive invocations - e.g. `dismiss` is called from `onResume` lifecycle callback of a - * component being shown. All recursive invocations are queued and performed one by one once - * the current navigation is finished. - * - * Should be called on the main thread. - * - * @param transformer transforms the current configuration to a new one, `null` means that the - * component is not shown. - * @param onComplete called when the navigation is finished (either synchronously or asynchronously). - */ - fun navigate( - transformer: (configuration: C?) -> C?, - onComplete: (newConfiguration: C?, oldConfiguration: C?) -> Unit, - ) -} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigatorExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigatorExt.kt deleted file mode 100644 index ff77abe8b..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigatorExt.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.arkivanov.decompose.router.overlay - -/** - * A convenience method for [OverlayNavigator.navigate]. - */ -@Deprecated(message = "Please use Child Slot API") -fun OverlayNavigator.navigate(transformer: (configuration: C?) -> C?) { - navigate(transformer = transformer, onComplete = { _, _ -> }) -} - -/** - * Activates an overlay component represented by the provided [configuration], - * and dismisses (destroys) any currently active component. Does nothing if the provided [configuration] - * is equal to the currently active one. - * - * @param onComplete called when the navigation is finished (either synchronously or asynchronously). - */ -@Deprecated(message = "Please use Child Slot API") -fun OverlayNavigator.activate(configuration: C, onComplete: () -> Unit = {}) { - navigate(transformer = { configuration }, onComplete = { _, _ -> onComplete() }) -} - -/** - * Dismisses (destroys) the currently active overlay component, if any. - * - * @param onComplete called when the navigation is finished (either synchronously or asynchronously). - * The `isSuccess` argument is `true` if there was an active overlay, `false` otherwise. - */ -@Deprecated(message = "Please use Child Slot API") -fun OverlayNavigator.dismiss(onComplete: (isSuccess: Boolean) -> Unit = {}) { - navigate(transformer = { null }, onComplete = { _, oldConfiguration -> onComplete(oldConfiguration != null) }) -} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ValueExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ValueExt.kt deleted file mode 100644 index 444c341a5..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ValueExt.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.arkivanov.decompose.router.overlay - -import com.arkivanov.decompose.Child -import com.arkivanov.decompose.value.Value - -@Suppress("DeprecatedCallableAddReplaceWith") -@Deprecated(message = "Please use Child Slot API") -val Value>.overlay: Child.Created? get() = value.overlay diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayIntegrationTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayIntegrationTest.kt deleted file mode 100644 index 5dccc8e2d..000000000 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayIntegrationTest.kt +++ /dev/null @@ -1,316 +0,0 @@ -package com.arkivanov.decompose.router.overlay - -import com.arkivanov.decompose.Child -import com.arkivanov.decompose.ComponentContext -import com.arkivanov.decompose.DefaultComponentContext -import com.arkivanov.decompose.router.TestInstance -import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher -import com.arkivanov.decompose.value.Value -import com.arkivanov.decompose.value.getValue -import com.arkivanov.essenty.backhandler.BackDispatcher -import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher -import com.arkivanov.essenty.instancekeeper.getOrCreate -import com.arkivanov.essenty.lifecycle.Lifecycle -import com.arkivanov.essenty.lifecycle.LifecycleRegistry -import com.arkivanov.essenty.lifecycle.resume -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.Parcelize -import com.arkivanov.essenty.statekeeper.consume -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertNotNull -import kotlin.test.assertNotSame -import kotlin.test.assertSame -import kotlin.test.assertTrue - -@Suppress("TestFunctionName") -class ChildOverlayIntegrationTest { - - private val navigation = OverlayNavigation() - private val lifecycle = LifecycleRegistry() - private val stateKeeperDispatcher = TestStateKeeperDispatcher() - private val instanceKeeperDispatcher = InstanceKeeperDispatcher() - private val backDispatcher = BackDispatcher() - - private val context = - DefaultComponentContext( - lifecycle = lifecycle, - stateKeeper = stateKeeperDispatcher, - instanceKeeper = instanceKeeperDispatcher, - backHandler = backDispatcher, - ) - - @BeforeTest - fun before() { - lifecycle.resume() - } - - @Test - fun WHEN_created_without_configuration_THEN_overlay_not_active() { - val overlay by context.childOverlay(initialConfiguration = null) - - overlay.assertOverlay(null) - } - - @Test - fun WHEN_created_with_configuration_THEN_overlay_active() { - val overlay by context.childOverlay(initialConfiguration = Config(1)) - - overlay.assertOverlay(1) - } - - @Test - fun GIVEN_not_active_WHEN_activate_THEN_overlay_active() { - val overlay by context.childOverlay(initialConfiguration = null) - - navigation.activate(Config(1)) - - overlay.assertOverlay(1) - } - - @Test - fun GIVEN_active_WHEN_dismiss_THEN_overlay_not_active() { - val overlay by context.childOverlay(initialConfiguration = Config(1)) - - navigation.dismiss() - - overlay.assertOverlay(null) - } - - @Test - fun GIVEN_active_WHEN_activate_with_same_configuration_THEN_same_overlay_active() { - val overlay by context.childOverlay(initialConfiguration = Config(1)) - - navigation.activate(Config(1)) - - overlay.assertOverlay(1) - } - - @Test - fun GIVEN_active_WHEN_activate_with_same_configuration_THEN_same_instance_active() { - val overlay by context.childOverlay(initialConfiguration = Config(1)) - val instance = overlay.requireOverlay().instance - - navigation.activate(Config(1)) - - assertSame(instance, overlay.overlay?.instance) - } - - @Test - fun GIVEN_active_WHEN_activate_with_other_configuration_THEN_other_overlay_active() { - val overlay by context.childOverlay(initialConfiguration = Config(1)) - - navigation.activate(Config(2)) - - overlay.assertOverlay(2) - } - - @Test - fun GIVEN_not_active_WHEN_activate_THEN_lifecycle_resumed() { - val overlay by context.childOverlay(initialConfiguration = null) - - navigation.activate(Config(1)) - - assertEquals(Lifecycle.State.RESUMED, overlay.requireOverlay().instance.lifecycle.state) - } - - @Test - fun GIVEN_active_WHEN_dismiss_THEN_lifecycle_destroyed() { - val overlay by context.childOverlay(initialConfiguration = Config(1)) - val lifecycle = overlay.requireOverlay().instance.lifecycle - - navigation.dismiss() - - assertEquals(Lifecycle.State.DESTROYED, lifecycle.state) - } - - @Test - fun GIVEN_not_active_WHEN_parent_backDispatcher_isEnabled_THEN_false() { - context.childOverlay(initialConfiguration = null) - - assertFalse(backDispatcher.isEnabled) - } - - @Test - fun GIVEN_active_WHEN_parent_backDispatcher_isEnabled_THEN_true() { - context.childOverlay(initialConfiguration = Config(1)) - - assertTrue(backDispatcher.isEnabled) - } - - @Test - fun GIVEN_active_WHEN_back_pressed_THEN_overlay_not_active() { - val overlay by context.childOverlay(initialConfiguration = Config(1)) - - backDispatcher.back() - - overlay.assertOverlay(null) - } - - @Test - fun GIVEN_dismissed_via_back_pressed_WHEN_parent_backDispatcher_isEnabled_THEN_false() { - context.childOverlay(initialConfiguration = Config(1)) - backDispatcher.back() - - assertFalse(backDispatcher.isEnabled) - } - - @Test - fun GIVEN_persistent_WHEN_recreated_THEN_overlay_active() { - val oldStateKeeper = TestStateKeeperDispatcher() - val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - oldContext.childOverlay(initialConfiguration = Config(1), persistent = true) - - val savedState = oldStateKeeper.save() - val newStateKeeper = TestStateKeeperDispatcher(savedState) - val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) - val newOverlay by newContext.childOverlay(initialConfiguration = null, persistent = true) - - newOverlay.assertOverlay(1) - } - - @Test - fun GIVEN_persistent_WHEN_recreated_THEN_overlay_state_restored() { - val oldStateKeeper = TestStateKeeperDispatcher() - val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - val overlay by oldContext.childOverlay(initialConfiguration = Config(1), persistent = true) - overlay.requireOverlay().instance.stateKeeper.register("key") { Config(10) } - - val savedState = oldStateKeeper.save() - val newStateKeeper = TestStateKeeperDispatcher(savedState) - val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) - val newOverlay by newContext.childOverlay(initialConfiguration = null, persistent = true) - val restoredState = newOverlay.requireOverlay().instance.stateKeeper.consume("key") - - assertEquals(Config(10), restoredState) - } - - @Test - fun GIVEN_not_persistent_WHEN_recreated_THEN_overlay_not_active() { - val oldStateKeeper = TestStateKeeperDispatcher() - val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - oldContext.childOverlay(initialConfiguration = Config(1), persistent = false) - - val savedState = oldStateKeeper.save() - val newStateKeeper = TestStateKeeperDispatcher(savedState) - val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) - val newOverlay by newContext.childOverlay(initialConfiguration = null, persistent = false) - - newOverlay.assertOverlay(null) - } - - @Test - fun WHEN_created_persistent_THEN_registered_in_parent_StateKeeper() { - context.childOverlay(initialConfiguration = Config(1), persistent = true) - - stateKeeperDispatcher.assertSupplierRegistered(key = "key") - } - - @Test - fun GIVEN_persistent_WHEN_recreated_THEN_overlay_instance_retained() { - val oldStateKeeper = TestStateKeeperDispatcher() - val instanceKeeper = InstanceKeeperDispatcher() - val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldOverlay by oldContext.childOverlay(initialConfiguration = Config(1), persistent = true) - val oldInstance = oldOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance) - - val savedState = oldStateKeeper.save() - val newStateKeeper = TestStateKeeperDispatcher(savedState) - val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) - val newOverlay by newContext.childOverlay(initialConfiguration = null, persistent = true) - val retainedInstance = newOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance) - - assertSame(oldInstance, retainedInstance) - } - - @Test - fun GIVEN_persistent_WHEN_recreated_THEN_overlay_instance_not_destroyed() { - val oldStateKeeper = TestStateKeeperDispatcher() - val instanceKeeper = InstanceKeeperDispatcher() - val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldOverlay by oldContext.childOverlay(initialConfiguration = Config(1), persistent = true) - val instance = oldOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance) - - val savedState = oldStateKeeper.save() - val newStateKeeper = TestStateKeeperDispatcher(savedState) - val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) - newContext.childOverlay(initialConfiguration = null, persistent = true) - - assertFalse(instance.isDestroyed) - } - - @Test - fun WHEN_created_persistent_THEN_registered_in_parent_InstanceKeeper() { - context.childOverlay(initialConfiguration = Config(1), persistent = true) - - assertNotNull(instanceKeeperDispatcher.get(key = "key")) - } - - @Test - fun GIVEN_not_persistent_WHEN_recreated_THEN_overlay_instance_not_retained() { - val oldStateKeeper = TestStateKeeperDispatcher() - val instanceKeeper = InstanceKeeperDispatcher() - val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldOverlay by oldContext.childOverlay(initialConfiguration = Config(1), persistent = false) - val oldInstance = oldOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance) - - val savedState = oldStateKeeper.save() - val newStateKeeper = TestStateKeeperDispatcher(savedState) - val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) - val newOverlay by newContext.childOverlay(initialConfiguration = Config(1), persistent = false) - val newInstance = newOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance) - - assertNotSame(oldInstance, newInstance) - } - - @Test - fun GIVEN_not_persistent_WHEN_recreated_THEN_overlay_instance_destroyed() { - val oldStateKeeper = TestStateKeeperDispatcher() - val instanceKeeper = InstanceKeeperDispatcher() - val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldOverlay by oldContext.childOverlay(initialConfiguration = Config(1), persistent = false) - val instance = oldOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance) - - val savedState = oldStateKeeper.save() - val newStateKeeper = TestStateKeeperDispatcher(savedState) - val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) - newContext.childOverlay(initialConfiguration = null, persistent = false) - - assertTrue(instance.isDestroyed) - } - - private fun ComponentContext.childOverlay( - initialConfiguration: Config?, - persistent: Boolean = true, - ): Value> = - childOverlay( - source = navigation, - key = "key", - initialConfiguration = { initialConfiguration }, - handleBackButton = true, - persistent = persistent, - childFactory = ::Component, - ) - - private fun ChildOverlay.requireOverlay(): Child.Created = - requireNotNull(overlay) - - private fun ChildOverlay.assertOverlay(id: Int?) { - assertEquals(id, overlay?.configuration?.id) - assertEquals(id, overlay?.instance?.id) - } - - @Parcelize - private data class Config( - val id: Int, - ) : Parcelable - - private class Component( - config: Config, - componentContext: ComponentContext, - ) : ComponentContext by componentContext { - val id: Int = config.id - } -} diff --git a/extensions-compose-jetbrains/api/android/extensions-compose-jetbrains.api b/extensions-compose-jetbrains/api/android/extensions-compose-jetbrains.api index ef01c0bf3..3f7c68e9e 100644 --- a/extensions-compose-jetbrains/api/android/extensions-compose-jetbrains.api +++ b/extensions-compose-jetbrains/api/android/extensions-compose-jetbrains.api @@ -48,13 +48,6 @@ public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/Ch public static final fun Children (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V } -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/ComposableSingletons$SlideKt { - public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/ComposableSingletons$SlideKt; - public static field lambda-1 Lkotlin/jvm/functions/Function5; - public fun ()V - public final fun getLambda-1$extensions_compose_jetbrains_release ()Lkotlin/jvm/functions/Function5; -} - public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction : java/lang/Enum { public static final field ENTER_BACK Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; public static final field ENTER_FRONT Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; @@ -83,9 +76,7 @@ public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/an } public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/SlideKt { - public static final synthetic fun slide (Landroidx/compose/animation/core/FiniteAnimationSpec;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; public static final fun slide (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static synthetic fun slide$default (Landroidx/compose/animation/core/FiniteAnimationSpec;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; public static synthetic fun slide$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; } diff --git a/extensions-compose-jetbrains/api/jvm/extensions-compose-jetbrains.api b/extensions-compose-jetbrains/api/jvm/extensions-compose-jetbrains.api index 1c6669b78..df381f2ee 100644 --- a/extensions-compose-jetbrains/api/jvm/extensions-compose-jetbrains.api +++ b/extensions-compose-jetbrains/api/jvm/extensions-compose-jetbrains.api @@ -60,13 +60,6 @@ public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/Ch public static final fun Children (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V } -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/ComposableSingletons$SlideKt { - public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/ComposableSingletons$SlideKt; - public static field lambda-1 Lkotlin/jvm/functions/Function5; - public fun ()V - public final fun getLambda-1$extensions_compose_jetbrains ()Lkotlin/jvm/functions/Function5; -} - public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction : java/lang/Enum { public static final field ENTER_BACK Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; public static final field ENTER_FRONT Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; @@ -95,9 +88,7 @@ public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/an } public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/SlideKt { - public static final synthetic fun slide (Landroidx/compose/animation/core/FiniteAnimationSpec;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; public static final fun slide (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static synthetic fun slide$default (Landroidx/compose/animation/core/FiniteAnimationSpec;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; public static synthetic fun slide$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; } diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Slide.kt b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Slide.kt index 6722e88a1..f6bb10182 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Slide.kt +++ b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Slide.kt @@ -6,18 +6,6 @@ import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.Modifier import androidx.compose.ui.layout.layout -/** - * A simple sliding animation. Children enter from one side and exit to another side. - */ -@Deprecated( - message = "Please use the new slide StackAnimator with orientation specification", - level = DeprecationLevel.HIDDEN -) -fun slide(animationSpec: FiniteAnimationSpec = tween()): StackAnimator = - stackAnimator(animationSpec = animationSpec) { factor, _, content -> - content(Modifier.offsetXFactor(factor = factor)) - } - /** * A simple sliding animation. Children enter from one side and exit to another side. */ From a83381b41458f211089eee6f84d3bbce5b22d6c8 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Mon, 4 Dec 2023 21:55:59 +0000 Subject: [PATCH 27/89] Renamed predictiveBackAnimation animation argument to fallbackAnimation --- .../jetbrains/stack/animation/StackAnimation.kt | 2 +- .../predictiveback/PredictiveBackAnimation.kt | 8 ++++---- .../sample/shared/counters/CountersContent.kt | 13 ++++--------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation.kt b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation.kt index cb8afea66..c2717ff9b 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation.kt +++ b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation.kt @@ -23,7 +23,7 @@ fun interface StackAnimation { * Creates an implementation of [StackAnimation] that allows different [StackAnimator]s. * * FaultyDecomposeApi. Please note that this API uses `movableContentOf` Compose API. - * Even though `movableContentOf` is not marked as experimental, it is known to contain bugs, + * Even though `movableContentOf` is not marked as experimental, it was known to contain bugs, * e.g. https://issuetracker.google.com/issues/270656235, https://issuetracker.google.com/issues/290343159. * * @param disableInputDuringAnimation disables input and touch events while animating, default value is `true`. diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimation.kt b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimation.kt index b941d1e6f..3b46cd641 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimation.kt +++ b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimation.kt @@ -23,12 +23,12 @@ import com.arkivanov.essenty.backhandler.BackHandler import kotlinx.coroutines.launch /** - * Wraps the provided [animation], handles the predictive back gesture and animates + * Wraps the provided [fallbackAnimation], handles the predictive back gesture and animates * the transition from the current [Child] to the previous one. * Calls [onBack] when the animation is finished. * * @param backHandler a source of the predictive back gesture events, see [BackHandler]. - * @param animation a [StackAnimation] for regular transitions. + * @param fallbackAnimation a [StackAnimation] for regular transitions. * @param selector a selector function that is called when the predictive back gesture begins, * returns [PredictiveBackAnimatable] responsible for animations. * @param onBack a callback that is called when the gesture is finished. @@ -36,7 +36,7 @@ import kotlinx.coroutines.launch @ExperimentalDecomposeApi fun predictiveBackAnimation( backHandler: BackHandler, - animation: StackAnimation? = null, + fallbackAnimation: StackAnimation? = null, selector: ( initialBackEvent: BackEvent, exitChild: Child.Created, @@ -48,7 +48,7 @@ fun predictiveBackAnimation( ): StackAnimation = PredictiveBackAnimation( backHandler = backHandler, - animation = animation ?: emptyStackAnimation(), + animation = fallbackAnimation ?: emptyStackAnimation(), selector = selector, onBack = onBack, ) diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt index b8b8dc86d..062214173 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt @@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.decompose.FaultyDecomposeApi import com.arkivanov.decompose.extensions.compose.jetbrains.stack.Children import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.fade import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.plus @@ -16,6 +18,7 @@ import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.scal import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimation import com.arkivanov.sample.shared.counters.counter.CounterContent +@OptIn(ExperimentalDecomposeApi::class, FaultyDecomposeApi::class) @Composable internal fun CountersContent(component: CountersComponent, modifier: Modifier = Modifier) { Children( @@ -23,15 +26,7 @@ internal fun CountersContent(component: CountersComponent, modifier: Modifier = modifier = modifier, animation = predictiveBackAnimation( backHandler = component.backHandler, - // Workaround for https://issuetracker.google.com/issues/270656235 - animation = stackAnimation(fade() + scale()), -// animation = stackAnimation { _, _, direction -> -// if (direction.isFront) { -// slide() + fade() -// } else { -// scale(frontFactor = 1F, backFactor = 0.7F) + fade() -// } -// }, + fallbackAnimation = stackAnimation(fade() + scale()), onBack = component::onBackClicked, ), ) { From 5df6c165dd1dce24520c45c161ce40705d845057 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 3 Dec 2023 20:43:29 +0000 Subject: [PATCH 28/89] Complete migration to kotlinx-serialization --- build.gradle.kts | 1 - decompose/api/android/decompose.api | 38 ----- decompose/api/jvm/decompose.api | 6 - decompose/build.gradle.kts | 9 +- .../arkivanov/decompose/RetainedComponent.kt | 9 +- .../decompose/statekeeper/ParcelableStub.kt | 18 --- .../decompose/ParcelizeDeprecatedMessage.kt | 4 - .../arkivanov/decompose/router/SavedData.kt | 32 ----- .../decompose/router/children/ChildItem.kt | 4 +- .../router/children/ChildItemFactory.kt | 4 +- .../router/children/ChildrenFactory.kt | 65 ++------- .../router/children/ChildrenNavigator.kt | 8 +- .../children/DefaultChildItemFactory.kt | 4 +- .../router/pages/ChildPagesFactory.kt | 134 ++---------------- .../decompose/router/slot/ChildSlotFactory.kt | 76 +--------- .../router/stack/ChildStackFactory.kt | 118 +-------------- .../decompose/statekeeper/ChildStateKeeper.kt | 8 +- .../ChildContextWithLifecycleTest.kt | 12 +- .../ChildContextWithoutLifecycleTest.kt | 13 +- .../com/arkivanov/decompose/TestUtils.kt | 11 ++ .../children/ChildrenBackPressedTest.kt | 24 ++-- .../router/children/ChildrenBasicTest.kt | 10 +- .../router/children/ChildrenLifecycleTest.kt | 44 +++--- .../children/ChildrenRetainedInstanceTest.kt | 28 ++-- .../router/children/ChildrenSavedStateTest.kt | 89 ++++++------ .../router/children/ChildrenTestBase.kt | 63 ++++---- .../router/pages/BaseChildPagesTest.kt | 39 ++--- .../router/pages/ChildPagesBackButtonTest.kt | 6 +- .../router/pages/ChildPagesClearTest.kt | 6 +- .../router/pages/ChildPagesIntegrationTest.kt | 32 ++--- .../router/pages/ChildPagesSavedStateTest.kt | 6 +- .../router/pages/ChildPagesSelectFirstTest.kt | 6 +- .../router/pages/ChildPagesSelectLastTest.kt | 6 +- .../router/pages/ChildPagesSelectNextTest.kt | 12 +- .../router/pages/ChildPagesSelectPrevTest.kt | 12 +- .../router/pages/ChildPagesSelectTest.kt | 12 +- .../router/slot/ChildSlotIntegrationTest.kt | 87 ++++++------ .../router/stack/ChildStackIntegrationTest.kt | 39 +++-- .../decompose/statekeeper/ParcelableStub.kt | 5 - .../statekeeper/TestParcelableContainer.kt | 20 --- .../statekeeper/TestStateKeeperDispatcher.kt | 55 +++---- .../DefaultWebHistoryControllerTest.kt | 3 +- .../decompose/statekeeper/ParcelableStub.kt | 5 - deps.versions.toml | 6 +- .../com/arkivanov/sample/app/MainActivity.kt | 1 + sample/app-desktop/saved_state.dat | 1 + .../kotlin/com/arkivanov/sample/app/Main.kt | 30 +--- .../UserInterfaceState.xcuserstate | Bin 27379 -> 27977 bytes .../app-ios-compose/iOSApp.swift | 11 +- sample/app-ios/app-ios/app_iosApp.swift | 17 +-- .../arkivanov/decompose/sample/app/Main.kt | 2 + sample/shared/compose/build.gradle.kts | 3 - sample/shared/shared/build.gradle.kts | 12 +- .../com/arkivanov/sample/shared/Json.kt | 8 ++ .../root/RootComponentIntegrationTest.kt | 2 + .../sample/shared/StateKeeperUtils.kt | 25 ++++ .../sample/shared/StateKeeperUtils.kt | 24 ++++ 57 files changed, 423 insertions(+), 902 deletions(-) delete mode 100644 decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/ParcelizeDeprecatedMessage.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/SavedData.kt create mode 100644 decompose/src/commonTest/kotlin/com/arkivanov/decompose/TestUtils.kt delete mode 100644 decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt delete mode 100644 decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestParcelableContainer.kt delete mode 100644 decompose/src/nonAndroidTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt create mode 100644 sample/app-desktop/saved_state.dat create mode 100644 sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/Json.kt create mode 100644 sample/shared/shared/src/iosMain/kotlin/com/arkivanov/sample/shared/StateKeeperUtils.kt create mode 100644 sample/shared/shared/src/jvmMain/kotlin/com/arkivanov/sample/shared/StateKeeperUtils.kt diff --git a/build.gradle.kts b/build.gradle.kts index 0197ba62c..3b8820646 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,7 +20,6 @@ buildscript { classpath(deps.android.gradle) classpath(deps.jetbrains.compose.composeGradlePlug) classpath(deps.jetbrains.kotlinx.binaryCompatibilityValidator) - classpath(deps.parcelizeDarwin.gradlePlug) classpath(deps.jetbrains.kotlin.serializationGradlePlug) } } diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 1ead9a197..bbb26bb14 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -82,14 +82,6 @@ public final class com/arkivanov/decompose/errorhandler/ErrorHandlersKt { public static final fun setOnDecomposeError (Lkotlin/jvm/functions/Function1;)V } -public final class com/arkivanov/decompose/router/ParcelableJson$Creator : android/os/Parcelable$Creator { - public fun ()V - public final fun createFromParcel (Landroid/os/Parcel;)Lcom/arkivanov/decompose/router/ParcelableJson; - public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; - public final fun newArray (I)[Lcom/arkivanov/decompose/router/ParcelableJson; - public synthetic fun newArray (I)[Ljava/lang/Object; -} - public abstract interface class com/arkivanov/decompose/router/children/ChildNavState { public abstract fun getConfiguration ()Ljava/lang/Object; public abstract fun getStatus ()Lcom/arkivanov/decompose/router/children/ChildNavState$Status; @@ -120,14 +112,6 @@ public abstract interface class com/arkivanov/decompose/router/children/Navigati public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V } -public final class com/arkivanov/decompose/router/children/SavedState$Creator : android/os/Parcelable$Creator { - public fun ()V - public final fun createFromParcel (Landroid/os/Parcel;)Lcom/arkivanov/decompose/router/children/SavedState; - public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; - public final fun newArray (I)[Lcom/arkivanov/decompose/router/children/SavedState; - public synthetic fun newArray (I)[Ljava/lang/Object; -} - public final class com/arkivanov/decompose/router/children/SimpleChildNavState : com/arkivanov/decompose/router/children/ChildNavState { public fun (Ljava/lang/Object;Lcom/arkivanov/decompose/router/children/ChildNavState$Status;)V public final fun component1 ()Ljava/lang/Object; @@ -164,10 +148,8 @@ public final class com/arkivanov/decompose/router/pages/ChildPages { public final class com/arkivanov/decompose/router/pages/ChildPagesFactoryKt { public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static final fun getDefaultPageStatus (ILcom/arkivanov/decompose/router/pages/Pages;)Lcom/arkivanov/decompose/router/children/ChildNavState$Status; } @@ -223,14 +205,6 @@ public final class com/arkivanov/decompose/router/pages/PagesNavigatorExtKt { public static synthetic fun selectPrev$default (Lcom/arkivanov/decompose/router/pages/PagesNavigator;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)V } -public final class com/arkivanov/decompose/router/pages/PagesSavedNavState$Creator : android/os/Parcelable$Creator { - public fun ()V - public final fun createFromParcel (Landroid/os/Parcel;)Lcom/arkivanov/decompose/router/pages/PagesSavedNavState; - public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; - public final fun newArray (I)[Lcom/arkivanov/decompose/router/pages/PagesSavedNavState; - public synthetic fun newArray (I)[Ljava/lang/Object; -} - public final class com/arkivanov/decompose/router/slot/ChildSlot { public fun ()V public fun (Lcom/arkivanov/decompose/Child$Created;)V @@ -246,10 +220,8 @@ public final class com/arkivanov/decompose/router/slot/ChildSlot { public final class com/arkivanov/decompose/router/slot/ChildSlotFactoryKt { public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } @@ -304,11 +276,9 @@ public final class com/arkivanov/decompose/router/stack/ChildStack { public final class com/arkivanov/decompose/router/stack/ChildStackFactoryKt { public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;ZZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } @@ -354,14 +324,6 @@ public final class com/arkivanov/decompose/router/stack/StackNavigatorExtKt { public static synthetic fun replaceCurrent$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V } -public final class com/arkivanov/decompose/router/stack/StackSavedNavState$Creator : android/os/Parcelable$Creator { - public fun ()V - public final fun createFromParcel (Landroid/os/Parcel;)Lcom/arkivanov/decompose/router/stack/StackSavedNavState; - public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; - public final fun newArray (I)[Lcom/arkivanov/decompose/router/stack/StackSavedNavState; - public synthetic fun newArray (I)[Ljava/lang/Object; -} - public final class com/arkivanov/decompose/router/stack/ValueExtKt { public static final fun getActive (Lcom/arkivanov/decompose/value/Value;)Lcom/arkivanov/decompose/Child$Created; public static final fun getBackStack (Lcom/arkivanov/decompose/value/Value;)Ljava/util/List; diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api index a0a1cebaa..97a65ab17 100644 --- a/decompose/api/jvm/decompose.api +++ b/decompose/api/jvm/decompose.api @@ -134,10 +134,8 @@ public final class com/arkivanov/decompose/router/pages/ChildPages { public final class com/arkivanov/decompose/router/pages/ChildPagesFactoryKt { public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static final fun getDefaultPageStatus (ILcom/arkivanov/decompose/router/pages/Pages;)Lcom/arkivanov/decompose/router/children/ChildNavState$Status; } @@ -208,10 +206,8 @@ public final class com/arkivanov/decompose/router/slot/ChildSlot { public final class com/arkivanov/decompose/router/slot/ChildSlotFactoryKt { public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/reflect/KClass;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } @@ -266,11 +262,9 @@ public final class com/arkivanov/decompose/router/stack/ChildStack { public final class com/arkivanov/decompose/router/stack/ChildStackFactoryKt { public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;ZZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } diff --git a/decompose/build.gradle.kts b/decompose/build.gradle.kts index 5b92f687f..bd77132b5 100644 --- a/decompose/build.gradle.kts +++ b/decompose/build.gradle.kts @@ -10,8 +10,6 @@ plugins { id("kotlin-multiplatform") id("com.android.library") id("kotlinx-serialization") - id("kotlin-parcelize") - id("com.arkivanov.parcelize.darwin") id("com.arkivanov.gradle.setup") } @@ -26,16 +24,12 @@ android { kotlin { setupSourceSets { val android by bundle() - val nonAndroid by bundle() - val nonNative by bundle() val darwin by bundle() val itvos by bundle() val js by bundle() val nonJs by bundle() - (nonAndroid + darwin + nonNative + nonJs) dependsOn common - (allSet - android) dependsOn nonAndroid - (allSet - nativeSet) dependsOn nonNative + (darwin) dependsOn common (allSet - js) dependsOn nonJs (iosSet + tvosSet) dependsOn itvos (darwinSet - iosSet - tvosSet + itvos) dependsOn darwin @@ -53,7 +47,6 @@ kotlin { api(deps.essenty.instanceKeeper) api(deps.essenty.backHandler) api(deps.jetbrains.kotlinx.kotlinxSerializationCore) - implementation(deps.jetbrains.kotlinx.kotlinxSerializationJson) } common.test.dependencies { diff --git a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt index 17a08c590..139b3ecad 100644 --- a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt +++ b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt @@ -22,9 +22,8 @@ import com.arkivanov.essenty.lifecycle.resume import com.arkivanov.essenty.lifecycle.start import com.arkivanov.essenty.lifecycle.stop import com.arkivanov.essenty.lifecycle.subscribe -import com.arkivanov.essenty.parcelable.ParcelableContainer +import com.arkivanov.essenty.statekeeper.SerializableContainer import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher -import com.arkivanov.essenty.statekeeper.consume import com.arkivanov.essenty.statekeeper.stateKeeper /** @@ -88,7 +87,7 @@ private fun O.retainedComponent( val holder = instanceKeeper.getOrCreate(key = key) { RetainedComponentHolder( - savedState = stateKeeper.consume(key = key), + savedState = stateKeeper.consume(key = key, strategy = SerializableContainer.serializer()), factory = factory, ) } @@ -114,7 +113,7 @@ private fun O.retainedComponent( }, ) - stateKeeper.register(key = key) { holder.stateKeeper.save() } + stateKeeper.register(key = key, strategy = SerializableContainer.serializer()) { holder.stateKeeper.save() } if (onBackPressedDispatcher != null) { val onBackPressedCallback = DelegateOnBackPressedCallback(holder.onBackPressedDispatcher) @@ -146,7 +145,7 @@ private class DelegateOnBackPressedCallback( } private class RetainedComponentHolder( - savedState: ParcelableContainer?, + savedState: SerializableContainer?, factory: (ComponentContext) -> T, ) : InstanceKeeper.Instance { val lifecycle: LifecycleRegistry = LifecycleRegistry() diff --git a/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt deleted file mode 100644 index 65e0515cf..000000000 --- a/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.arkivanov.decompose.statekeeper - -import android.os.Parcel -import com.arkivanov.essenty.parcelable.Parcelable - -actual class ParcelableStub actual constructor() : Parcelable { - - override fun describeContents(): Int = 0 - - override fun writeToParcel(dest: Parcel, flags: Int) { - } - - companion object CREATOR : android.os.Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): ParcelableStub = ParcelableStub() - - override fun newArray(size: Int): Array = arrayOfNulls(size) - } -} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ParcelizeDeprecatedMessage.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ParcelizeDeprecatedMessage.kt deleted file mode 100644 index 5c7d269bb..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ParcelizeDeprecatedMessage.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.arkivanov.decompose - -internal const val PARCELIZE_DEPRECATED_MESSAGE = - "Parcelize compiler plugin will not work with KMP and K2 compiler. Please use similar API based on kotlinx-serialization." diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/SavedData.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/SavedData.kt deleted file mode 100644 index 5d89739d7..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/SavedData.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.arkivanov.decompose.router - -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.ParcelableContainer -import com.arkivanov.essenty.parcelable.Parcelize -import com.arkivanov.essenty.parcelable.consumeRequired -import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.SerializationStrategy -import kotlinx.serialization.json.Json - -private val json = - Json { - allowStructuredMapKeys = true - } - -@Parcelize -private class ParcelableJson(val json: String) : Parcelable - -// Temporary interop until v3.0 -internal fun T.toParcelableContainer(strategy: SerializationStrategy): ParcelableContainer = - ParcelableContainer( - value = ParcelableJson( - json = json.encodeToString(serializer = strategy, value = this), - ), - ) - -// Temporary interop until v3.0 -internal fun ParcelableContainer.consumeRequired(strategy: DeserializationStrategy): T = - json.decodeFromString( - deserializer = strategy, - string = consumeRequired().json, - ) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItem.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItem.kt index d9f67f9d3..45f5b469c 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItem.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItem.kt @@ -3,7 +3,7 @@ package com.arkivanov.decompose.router.children import com.arkivanov.decompose.backhandler.ChildBackHandler import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher import com.arkivanov.essenty.lifecycle.LifecycleRegistry -import com.arkivanov.essenty.parcelable.ParcelableContainer +import com.arkivanov.essenty.statekeeper.SerializableContainer import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher internal sealed interface ChildItem { @@ -22,7 +22,7 @@ internal sealed interface ChildItem { data class Destroyed( override val configuration: C, - val savedState: ParcelableContainer? = null + val savedState: SerializableContainer? = null ) : ChildItem { override val instance: Nothing? = null } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItemFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItemFactory.kt index e7d2d0ea1..750dcab7f 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItemFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItemFactory.kt @@ -1,13 +1,13 @@ package com.arkivanov.decompose.router.children import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher -import com.arkivanov.essenty.parcelable.ParcelableContainer +import com.arkivanov.essenty.statekeeper.SerializableContainer internal interface ChildItemFactory { operator fun invoke( configuration: C, - savedState: ParcelableContainer? = null, + savedState: SerializableContainer? = null, instanceKeeperDispatcher: InstanceKeeperDispatcher? = null, ): ChildItem.Created } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt index 4996057fb..8d697c759 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt @@ -2,21 +2,16 @@ package com.arkivanov.decompose.router.children import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext -import com.arkivanov.decompose.PARCELIZE_DEPRECATED_MESSAGE import com.arkivanov.decompose.backhandler.child -import com.arkivanov.decompose.router.consumeRequired -import com.arkivanov.decompose.router.toParcelableContainer import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.instancekeeper.getOrCreate import com.arkivanov.essenty.lifecycle.doOnDestroy -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.ParcelableContainer -import com.arkivanov.essenty.parcelable.Parcelize -import com.arkivanov.essenty.parcelable.consumeRequired -import com.arkivanov.essenty.statekeeper.consume +import com.arkivanov.essenty.statekeeper.SerializableContainer +import com.arkivanov.essenty.statekeeper.consumeRequired import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable /** * A convenience method for the main [children] method. Allows having `Serializable` navigation state [N], @@ -39,7 +34,7 @@ fun ComponentContext.children( source = source, saveState = { state -> if (stateSerializer != null) { - state.toParcelableContainer(strategy = stateSerializer) + SerializableContainer(value = state, strategy = stateSerializer) } else { null } @@ -86,9 +81,9 @@ fun ComponentContext.children( * @param key a key of this `children` collection, must be unique if there are multiple * `children` used in the same component. * @param initialState an initial navigation state that should be used if there is no previously saved state. - * @param saveState a function that saves the provided navigation state into [ParcelableContainer]. + * @param saveState a function that saves the provided navigation state into [SerializableContainer]. * The navigation state is not saved if `null` is returned. - * @param restoreState a function that restores the navigation state from the provided [ParcelableContainer]. + * @param restoreState a function that restores the navigation state from the provided [SerializableContainer]. * If `null` is returned then [initialState] is used instead. * The restored navigation state must have the same amount of child configurations and in the same order. * The restored child [Statuses][ChildNavState.Status] can be any, e.g. a previously active child may become @@ -109,8 +104,8 @@ fun , S : Any> ComponentContext.child source: NavigationSource, key: String, initialState: () -> N, - saveState: (state: N) -> ParcelableContainer?, - restoreState: (container: ParcelableContainer) -> N?, + saveState: (state: N) -> SerializableContainer?, + restoreState: (container: SerializableContainer) -> N?, navTransformer: (state: N, event: E) -> N, stateMapper: (state: N, children: List>) -> S, onStateChanged: (newState: N, oldState: N?) -> Unit = { _, _ -> }, @@ -121,7 +116,7 @@ fun , S : Any> ComponentContext.child val mainBackHandler = backHandler.child() val navigator = - stateKeeper.consume(key = key).let { savedState -> + stateKeeper.consume(key = key, strategy = SavedState.serializer()).let { savedState -> val restoredNavState: N? = savedState?.navState?.let(restoreState) ChildrenNavigator( @@ -137,7 +132,7 @@ fun , S : Any> ComponentContext.child ) } - stateKeeper.register(key = key) { + stateKeeper.register(key = key, strategy = SavedState.serializer()) { saveState(navigator.navState)?.let { savedState -> SavedState( navState = savedState, @@ -193,40 +188,8 @@ fun , S : Any> ComponentContext.child return state } -/** - * A convenience method for the main [children] method. Allows having [Parcelable] navigation state [N], - * so it's automatically saved and restored. This method can be used if the custom save/restore logic - * is not required. - */ -@Suppress("DeprecatedCallableAddReplaceWith") -@Deprecated(message = PARCELIZE_DEPRECATED_MESSAGE) -inline fun ComponentContext.children( - source: NavigationSource, - key: String, - noinline initialState: () -> N, - noinline navTransformer: (state: N, event: E) -> N, - noinline stateMapper: (state: N, children: List>) -> S, - noinline onStateChanged: (newState: N, oldState: N?) -> Unit = { _, _ -> }, - noinline onEventComplete: (event: E, newState: N, oldState: N) -> Unit = { _, _, _ -> }, - noinline backTransformer: (state: N) -> (() -> N)? = { null }, - noinline childFactory: (configuration: C, componentContext: ComponentContext) -> T, -): Value where N : NavState, N : Parcelable = - children( - source = source, - key = key, - initialState = initialState, - saveState = { ParcelableContainer(it) }, - restoreState = { it.consumeRequired(N::class) }, - navTransformer = navTransformer, - stateMapper = stateMapper, - onStateChanged = onStateChanged, - onEventComplete = onEventComplete, - backTransformer = backTransformer, - childFactory = childFactory, - ) - -@Parcelize +@Serializable private class SavedState( - val navState: ParcelableContainer, - val childState: List, -) : Parcelable + val navState: SerializableContainer, + val childState: List, +) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt index 2dad11de1..4c5d5e708 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt @@ -10,14 +10,14 @@ import com.arkivanov.essenty.lifecycle.destroy import com.arkivanov.essenty.lifecycle.doOnDestroy import com.arkivanov.essenty.lifecycle.resume import com.arkivanov.essenty.lifecycle.stop -import com.arkivanov.essenty.parcelable.ParcelableContainer +import com.arkivanov.essenty.statekeeper.SerializableContainer internal class ChildrenNavigator>( lifecycle: Lifecycle, retainedInstanceSupplier: (factory: () -> InstanceKeeper.Instance) -> InstanceKeeper.Instance, private val childItemFactory: ChildItemFactory, navState: N, - savedChildState: List?, + savedChildState: List?, ) { var navState: N = navState private set @@ -60,7 +60,7 @@ internal class ChildrenNavigator>( } } - private fun restore(navState: N, savedStates: List) { + private fun restore(navState: N, savedStates: List) { val retainedChildren = retainedInstance.items.associateByTo(HashMap(), Created::configuration) retainedInstance.items.clear() @@ -96,7 +96,7 @@ internal class ChildrenNavigator>( retainedChildren.values.forEach { it.instanceKeeperDispatcher.destroy() } } - fun saveChildState(): List = + fun saveChildState(): List = items.map { item -> when (item) { is Created -> item.stateKeeperDispatcher.save() diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt index 0bdf7e85f..b9b23df36 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt @@ -8,7 +8,7 @@ import com.arkivanov.essenty.backhandler.BackHandler import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.LifecycleRegistry -import com.arkivanov.essenty.parcelable.ParcelableContainer +import com.arkivanov.essenty.statekeeper.SerializableContainer import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher internal class DefaultChildItemFactory( @@ -19,7 +19,7 @@ internal class DefaultChildItemFactory( override fun invoke( configuration: C, - savedState: ParcelableContainer?, + savedState: SerializableContainer?, instanceKeeperDispatcher: InstanceKeeperDispatcher? ): ChildItem.Created { val componentLifecycleRegistry = LifecycleRegistry() diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt index 49792ee67..4c693e80d 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt @@ -2,22 +2,16 @@ package com.arkivanov.decompose.router.pages import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.decompose.PARCELIZE_DEPRECATED_MESSAGE import com.arkivanov.decompose.router.children.ChildNavState import com.arkivanov.decompose.router.children.ChildNavState.Status import com.arkivanov.decompose.router.children.NavState import com.arkivanov.decompose.router.children.SimpleChildNavState import com.arkivanov.decompose.router.children.children -import com.arkivanov.decompose.router.consumeRequired -import com.arkivanov.decompose.router.toParcelableContainer import com.arkivanov.decompose.value.Value -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.ParcelableContainer -import com.arkivanov.essenty.parcelable.Parcelize -import com.arkivanov.essenty.parcelable.consumeRequired +import com.arkivanov.essenty.statekeeper.SerializableContainer +import com.arkivanov.essenty.statekeeper.consumeRequired import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import kotlin.reflect.KClass /** * Initializes and manages a list of components with one selected (active) component. @@ -53,8 +47,10 @@ fun ComponentContext.childPages( source = source, savePages = { pages -> if (serializer != null) { - SerializablePages(items = pages.items, selectedIndex = pages.selectedIndex) - .toParcelableContainer(strategy = SerializablePages.serializer(serializer)) + SerializableContainer( + value = SerializablePages(items = pages.items, selectedIndex = pages.selectedIndex), + strategy = SerializablePages.serializer(serializer), + ) } else { null } @@ -90,113 +86,9 @@ private class SerializablePages( * @param source a source of navigation events. * @param initialPages an initial state of Child Pages that should be set * if there is no saved state. See [Pages] for more information. - * @param key a key of the list, must be unique if there are multiple Child Pages used in - * the same component. - * @param pageStatus a function that returns a [Status] of a page at a given index. - * By default, the currently selected page is [Status.ACTIVE], its two neighbours - * are [Status.INACTIVE], and the rest are [Status.DESTROYED]. You can implement your own - * logic, for example with circular behaviour. - * @param persistent determines whether the navigation state should pre preserved or not, - * default is `true`. - * @param handleBackButton determines whether the previous component should be automatically - * selected on back button press or not, default is `false`. - * @param childFactory a factory function that creates new child instances. - * @return an observable [Value] of [ChildPages]. - */ -@Suppress("DeprecatedCallableAddReplaceWith", "DEPRECATION") -@Deprecated(message = PARCELIZE_DEPRECATED_MESSAGE) -@ExperimentalDecomposeApi -inline fun ComponentContext.childPages( - source: PagesNavigationSource, - noinline initialPages: () -> Pages, - key: String = "DefaultChildPages", - noinline pageStatus: (index: Int, Pages) -> Status = ::getDefaultPageStatus, - persistent: Boolean = true, - handleBackButton: Boolean = false, - noinline childFactory: (configuration: C, ComponentContext) -> T, -): Value> = - childPages( - source = source, - initialPages = initialPages, - configurationClass = C::class, - key = key, - pageStatus = pageStatus, - persistent = persistent, - handleBackButton = handleBackButton, - childFactory = childFactory, - ) - -/** - * Initializes and manages a list of components with one selected (active) component. - * The list can be empty. - * - * @param source a source of navigation events. - * @param initialPages an initial state of Child Pages that should be set - * if there is no saved state. See [Pages] for more information. - * @param configurationClass a [KClass] of the component configurations. - * @param key a key of the list, must be unique if there are multiple Child Pages used in - * the same component. - * @param pageStatus a function that returns a [Status] of a page at a given index. - * By default, the currently selected page is [Status.ACTIVE], its two neighbours - * are [Status.INACTIVE], and the rest are [Status.DESTROYED]. You can implement your own - * logic, for example with circular behaviour. - * @param persistent determines whether the navigation state should pre preserved or not, - * default is `true`. - * @param handleBackButton determines whether the previous component should be automatically - * selected on back button press or not, default is `false`. - * @param childFactory a factory function that creates new child instances. - * @return an observable [Value] of [ChildPages]. - */ -@Deprecated(message = PARCELIZE_DEPRECATED_MESSAGE) -@ExperimentalDecomposeApi -fun ComponentContext.childPages( - source: PagesNavigationSource, - initialPages: () -> Pages, - configurationClass: KClass, - key: String = "DefaultChildPages", - pageStatus: (index: Int, Pages) -> Status = ::getDefaultPageStatus, - persistent: Boolean = true, - handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, -): Value> = - childPages( - source = source, - initialPages = initialPages, - savePages = { pages -> - if (persistent) { - ParcelableContainer( - PagesSavedNavState( - configurations = pages.items.map { ParcelableContainer(it) }, - selectedIndex = pages.selectedIndex, - ) - ) - } else { - null - } - }, - restorePages = { container -> - val state = container.consumeRequired() - Pages( - items = state.configurations.map { it.consumeRequired(configurationClass) }, - selectedIndex = state.selectedIndex, - ) - }, - key = key, - pageStatus = pageStatus, - handleBackButton = handleBackButton, - childFactory = childFactory, - ) - -/** - * Initializes and manages a list of components with one selected (active) component. - * The list can be empty. - * - * @param source a source of navigation events. - * @param initialPages an initial state of Child Pages that should be set - * if there is no saved state. See [Pages] for more information. - * @param savePages a function that saves the provided [Pages] state into [ParcelableContainer]. + * @param savePages a function that saves the provided [Pages] state into [SerializableContainer]. * The navigation state is not saved if `null` is returned. - * @param restorePages a function that restores the [Pages] state from the provided [ParcelableContainer]. + * @param restorePages a function that restores the [Pages] state from the provided [SerializableContainer]. * If `null` is returned then [initialPages] is used instead. * The restored [Pages] state must have the same amount of configurations and in the same order. * @param key a key of the list, must be unique if there are multiple Child Pages used in @@ -214,8 +106,8 @@ fun ComponentContext.childPages( fun ComponentContext.childPages( source: PagesNavigationSource, initialPages: () -> Pages, - savePages: (Pages) -> ParcelableContainer?, - restorePages: (ParcelableContainer) -> Pages?, + savePages: (Pages) -> SerializableContainer?, + restorePages: (SerializableContainer) -> Pages?, key: String = "DefaultChildPages", pageStatus: (index: Int, Pages) -> Status = ::getDefaultPageStatus, handleBackButton: Boolean = false, @@ -284,8 +176,8 @@ private data class PagesNavState( } } -@Parcelize +@Serializable private class PagesSavedNavState( - val configurations: List, + val configurations: List, val selectedIndex: Int, -) : Parcelable +) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt index 0c82404fa..3ddd61846 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt @@ -2,18 +2,14 @@ package com.arkivanov.decompose.router.slot import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext -import com.arkivanov.decompose.PARCELIZE_DEPRECATED_MESSAGE import com.arkivanov.decompose.router.children.ChildNavState.Status import com.arkivanov.decompose.router.children.NavState import com.arkivanov.decompose.router.children.SimpleChildNavState import com.arkivanov.decompose.router.children.children -import com.arkivanov.decompose.router.consumeRequired -import com.arkivanov.decompose.router.toParcelableContainer import com.arkivanov.decompose.value.Value -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.ParcelableContainer +import com.arkivanov.essenty.statekeeper.SerializableContainer +import com.arkivanov.essenty.statekeeper.consumeRequired import kotlinx.serialization.KSerializer -import kotlin.reflect.KClass /** * Initializes and manages a slot for one child component. @@ -42,7 +38,7 @@ fun ComponentContext.childSlot( source = source, saveConfiguration = { configuration -> if ((serializer != null) && (configuration != null)) { - configuration.toParcelableContainer(strategy = serializer) + SerializableContainer(value = configuration, strategy = serializer) } else { null } @@ -65,46 +61,9 @@ fun ComponentContext.childSlot( * The child component can be either active or dismissed (destroyed). * * @param source a source of navigation events. - * @param configurationClass a [KClass] of the component configurations. * @param key a key of the slot, must be unique within the parent (hosting) component. - * @param initialConfiguration a component configuration that should be shown if there is - * no saved state, return `null` to show nothing. - * @param persistent determines whether the navigation state should pre preserved or not, - * default is `true`. - * @param handleBackButton determines whether the child component should be automatically dismissed - * on back button press or not, default is `false`. - * @param childFactory a factory function that creates new child instances. - * @return an observable [Value] of [ChildSlot]. - */ -@Suppress("DeprecatedCallableAddReplaceWith") -@Deprecated(message = PARCELIZE_DEPRECATED_MESSAGE) -fun ComponentContext.childSlot( - source: SlotNavigationSource, - configurationClass: KClass, - key: String = "DefaultChildSlot", - initialConfiguration: () -> C? = { null }, - persistent: Boolean = true, - handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, -): Value> = - childSlot( - source = source, - saveConfiguration = { if (persistent) ParcelableContainer(it) else null }, - restoreConfiguration = { it.consume(configurationClass) }, - key = key, - initialConfiguration = initialConfiguration, - handleBackButton = handleBackButton, - childFactory = childFactory, - ) - -/** - * Initializes and manages a slot for one child component. - * The child component can be either active or dismissed (destroyed). - * - * @param source a source of navigation events. - * @param key a key of the slot, must be unique within the parent (hosting) component. - * @param saveConfiguration a function that saves the provided configuration into [ParcelableContainer]. - * @param restoreConfiguration a function that restores the configuration from the provided [ParcelableContainer]. + * @param saveConfiguration a function that saves the provided configuration into [SerializableContainer]. + * @param restoreConfiguration a function that restores the configuration from the provided [SerializableContainer]. * @param initialConfiguration a component configuration that should be shown if there is * no saved state, return `null` to show nothing. * @param handleBackButton determines whether the child component should be automatically dismissed @@ -114,8 +73,8 @@ fun ComponentContext.childSlot( */ fun ComponentContext.childSlot( source: SlotNavigationSource, - saveConfiguration: (C?) -> ParcelableContainer?, - restoreConfiguration: (ParcelableContainer) -> C?, + saveConfiguration: (C?) -> SerializableContainer?, + restoreConfiguration: (SerializableContainer) -> C?, key: String = "DefaultChildSlot", initialConfiguration: () -> C? = { null }, handleBackButton: Boolean = false, @@ -140,27 +99,6 @@ fun ComponentContext.childSlot( childFactory = childFactory, ) -/** - * A convenience extension function for [ComponentContext.childSlot]. - */ -inline fun ComponentContext.childSlot( - source: SlotNavigationSource, - key: String = "DefaultChildSlot", - noinline initialConfiguration: () -> C? = { null }, - persistent: Boolean = true, - handleBackButton: Boolean = false, - noinline childFactory: (configuration: C, ComponentContext) -> T, -): Value> = - childSlot( - source = source, - key = key, - configurationClass = C::class, - initialConfiguration = initialConfiguration, - persistent = persistent, - handleBackButton = handleBackButton, - childFactory = childFactory, - ) - private data class SlotNavState( val configuration: C?, ) : NavState { diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt index 675beba0a..84e9a87e0 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt @@ -2,21 +2,15 @@ package com.arkivanov.decompose.router.stack import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext -import com.arkivanov.decompose.PARCELIZE_DEPRECATED_MESSAGE import com.arkivanov.decompose.router.children.ChildNavState.Status import com.arkivanov.decompose.router.children.NavState import com.arkivanov.decompose.router.children.SimpleChildNavState import com.arkivanov.decompose.router.children.children -import com.arkivanov.decompose.router.consumeRequired -import com.arkivanov.decompose.router.toParcelableContainer import com.arkivanov.decompose.value.Value -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.ParcelableContainer -import com.arkivanov.essenty.parcelable.Parcelize -import com.arkivanov.essenty.parcelable.consumeRequired +import com.arkivanov.essenty.statekeeper.SerializableContainer +import com.arkivanov.essenty.statekeeper.consumeRequired import kotlinx.serialization.KSerializer import kotlinx.serialization.builtins.ListSerializer -import kotlin.reflect.KClass /** * Initializes and manages a stack of components. @@ -44,7 +38,7 @@ fun ComponentContext.childStack( source = source, saveStack = { stack -> if (serializer != null) { - stack.toParcelableContainer(strategy = ListSerializer(serializer)) + SerializableContainer(value = stack, strategy = ListSerializer(serializer)) } else { null } @@ -65,7 +59,6 @@ fun ComponentContext.childStack( /** * A convenience extension function for [ComponentContext.childStack]. */ -@Suppress("DeprecatedCallableAddReplaceWith") fun ComponentContext.childStack( source: StackNavigationSource, serializer: KSerializer?, @@ -89,59 +82,9 @@ fun ComponentContext.childStack( * @param source a source of navigation events. * @param initialStack a stack of component configurations (ordered from tail to head) that should be set * if there is no saved state, must be not empty and unique. - * @param configurationClass a [KClass] of the component configurations. - * @param key a key of the stack, must be unique if there are multiple stacks in the same component. - * @param persistent determines whether the navigation state should pre preserved or not, - * default is `true`. - * @param handleBackButton determines whether the stack should be automatically popped on back button press or not, - * default is `false`. - * @param childFactory a factory function that creates new child instances. - * @return an observable [Value] of [ChildStack]. - */ -@Deprecated(message = PARCELIZE_DEPRECATED_MESSAGE) -fun ComponentContext.childStack( - source: StackNavigationSource, - initialStack: () -> List, - configurationClass: KClass, - key: String = "DefaultChildStack", - persistent: Boolean = true, - handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, -): Value> = - childStack( - source = source, - initialStack = initialStack, - saveStack = { stack -> - if (persistent) { - ParcelableContainer( - StackSavedNavState( - configurations = stack.map { ParcelableContainer(it) }, - ) - ) - } else { - null - } - }, - restoreStack = { container -> - container - .consumeRequired() - .configurations - .map { it.consumeRequired(configurationClass) } - }, - key = key, - handleBackButton = handleBackButton, - childFactory = childFactory, - ) - -/** - * Initializes and manages a stack of components. - * - * @param source a source of navigation events. - * @param initialStack a stack of component configurations (ordered from tail to head) that should be set - * if there is no saved state, must be not empty and unique. - * @param saveStack a function that saves the provided stack of configurations into [ParcelableContainer]. + * @param saveStack a function that saves the provided stack of configurations into [SerializableContainer]. * The navigation state is not saved if `null` is returned. - * @param restoreStack a function that restores the stack of configuration from the provided [ParcelableContainer]. + * @param restoreStack a function that restores the stack of configuration from the provided [SerializableContainer]. * If `null` is returned then [initialStack] is used instead. * The restored stack must have the same amount of configurations and in the same order. * @param key a key of the stack, must be unique if there are multiple stacks in the same component. @@ -153,8 +96,8 @@ fun ComponentContext.childStack( fun ComponentContext.childStack( source: StackNavigationSource, initialStack: () -> List, - saveStack: (List) -> ParcelableContainer?, - restoreStack: (ParcelableContainer) -> List?, + saveStack: (List) -> SerializableContainer?, + restoreStack: (SerializableContainer) -> List?, key: String = "DefaultChildStack", handleBackButton: Boolean = false, childFactory: (configuration: C, ComponentContext) -> T, @@ -188,48 +131,6 @@ fun ComponentContext.childStack( childFactory = childFactory, ) -/** - * A convenience extension function for [ComponentContext.childStack]. - */ -inline fun ComponentContext.childStack( - source: StackNavigationSource, - noinline initialStack: () -> List, - key: String = "DefaultChildStack", - persistent: Boolean = true, - handleBackButton: Boolean = false, - noinline childFactory: (configuration: C, ComponentContext) -> T -): Value> = - childStack( - source = source, - initialStack = initialStack, - configurationClass = C::class, - key = key, - persistent = persistent, - handleBackButton = handleBackButton, - childFactory = childFactory, - ) - -/** - * A convenience extension function for [ComponentContext.childStack]. - */ -inline fun ComponentContext.childStack( - source: StackNavigationSource, - initialConfiguration: C, - key: String = "DefaultChildStack", - persistent: Boolean = true, - handleBackButton: Boolean = false, - noinline childFactory: (configuration: C, ComponentContext) -> T -): Value> = - childStack( - source = source, - initialStack = { listOf(initialConfiguration) }, - configurationClass = C::class, - key = key, - persistent = persistent, - handleBackButton = handleBackButton, - childFactory = childFactory, - ) - private data class StackNavState( val configurations: List, ) : NavState { @@ -246,8 +147,3 @@ private data class StackNavState( ) } } - -@Parcelize -private class StackSavedNavState( - val configurations: List, -) : Parcelable diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/statekeeper/ChildStateKeeper.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/statekeeper/ChildStateKeeper.kt index 8267c4e12..6db7b7ac7 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/statekeeper/ChildStateKeeper.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/statekeeper/ChildStateKeeper.kt @@ -3,19 +3,19 @@ package com.arkivanov.decompose.statekeeper import com.arkivanov.decompose.isDestroyed import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.doOnDestroy +import com.arkivanov.essenty.statekeeper.SerializableContainer import com.arkivanov.essenty.statekeeper.StateKeeper import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher -import com.arkivanov.essenty.statekeeper.consume internal fun StateKeeper.child(key: String, lifecycle: Lifecycle? = null): StateKeeper { check(!isRegistered(key = key)) { "The key \"$key\" is already in use." } - val stateKeeper = StateKeeperDispatcher(consume(key)) + val stateKeeper = StateKeeperDispatcher(consume(key = key, strategy = SerializableContainer.serializer())) if (lifecycle == null) { - register(key, stateKeeper::save) + register(key = key, strategy = SerializableContainer.serializer(), supplier = stateKeeper::save) } else if (!lifecycle.isDestroyed) { - register(key, stateKeeper::save) + register(key = key, strategy = SerializableContainer.serializer(), supplier = stateKeeper::save) lifecycle.doOnDestroy { unregister(key) } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithLifecycleTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithLifecycleTest.kt index 5b117a958..101ec92d5 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithLifecycleTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithLifecycleTest.kt @@ -2,7 +2,6 @@ package com.arkivanov.decompose import com.arkivanov.decompose.backhandler.TestBackDispatcher import com.arkivanov.decompose.router.TestInstance -import com.arkivanov.decompose.statekeeper.ParcelableStub import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher @@ -15,7 +14,7 @@ import com.arkivanov.essenty.lifecycle.destroy import com.arkivanov.essenty.lifecycle.resume import com.arkivanov.essenty.lifecycle.start import com.arkivanov.essenty.lifecycle.stop -import com.arkivanov.essenty.parcelable.ParcelableContainer +import com.arkivanov.essenty.statekeeper.SerializableContainer import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -90,17 +89,16 @@ class ChildContextWithLifecycleTest { fun WHEN_recreated_THEN_child_state_restored() { val context = TestContext() val childContext = context.childContext(key = "key", lifecycle = LifecycleRegistry()) - val savedChildState = ParcelableStub() - childContext.stateKeeper.register("child_key") { savedChildState } + childContext.stateKeeper.register("child_key") { "savedChildState" } context.lifecycle.resume() val savedParentState = context.stateKeeper.save() context.lifecycle.destroy() val newContext = TestContext(savedParentState) val newChild = newContext.childContext(key = "key", lifecycle = LifecycleRegistry()) - val restoredChildState = newChild.stateKeeper.consume("child_key", ParcelableStub::class) + val restoredChildState = newChild.stateKeeper.consume("child_key") - assertEquals(savedChildState, restoredChildState) + assertEquals("savedChildState", restoredChildState) } @Test @@ -241,7 +239,7 @@ class ChildContextWithLifecycleTest { } private class TestContext( - savedState: ParcelableContainer? = null, + savedState: SerializableContainer? = null, override val instanceKeeper: InstanceKeeperDispatcher = InstanceKeeperDispatcher(), ) : ComponentContext { override val lifecycle: LifecycleRegistry = LifecycleRegistry() diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithoutLifecycleTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithoutLifecycleTest.kt index 8bc530654..690089c51 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithoutLifecycleTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithoutLifecycleTest.kt @@ -1,7 +1,6 @@ package com.arkivanov.decompose import com.arkivanov.decompose.router.TestInstance -import com.arkivanov.decompose.statekeeper.ParcelableStub import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.backhandler.BackDispatcher @@ -11,7 +10,8 @@ import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.destroy import com.arkivanov.essenty.lifecycle.resume -import com.arkivanov.essenty.parcelable.ParcelableContainer +import com.arkivanov.essenty.statekeeper.SerializableContainer +import kotlinx.serialization.builtins.serializer import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -47,17 +47,16 @@ class ChildContextWithoutLifecycleTest { fun WHEN_recreated_THEN_child_state_restored() { val context = TestContext() val childContext = context.childContext(key = "key") - val savedChildState = ParcelableStub() - childContext.stateKeeper.register("child_key") { savedChildState } + childContext.stateKeeper.register(key = "child_key", strategy = String.serializer()) { "savedChildState" } context.lifecycle.resume() val savedParentState = context.stateKeeper.save() context.lifecycle.destroy() val newContext = TestContext(savedParentState) val newChild = newContext.childContext(key = "key") - val restoredChildState = newChild.stateKeeper.consume("child_key", ParcelableStub::class) + val restoredChildState = newChild.stateKeeper.consume(key = "child_key", strategy = String.serializer()) - assertEquals(savedChildState, restoredChildState) + assertEquals("savedChildState", restoredChildState) } @Test @@ -151,7 +150,7 @@ class ChildContextWithoutLifecycleTest { } private class TestContext( - savedState: ParcelableContainer? = null, + savedState: SerializableContainer? = null, override val instanceKeeper: InstanceKeeperDispatcher = InstanceKeeperDispatcher(), ) : ComponentContext { override val lifecycle: LifecycleRegistry = LifecycleRegistry() diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/TestUtils.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/TestUtils.kt new file mode 100644 index 000000000..9239ed927 --- /dev/null +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/TestUtils.kt @@ -0,0 +1,11 @@ +package com.arkivanov.decompose + +import com.arkivanov.essenty.statekeeper.StateKeeper +import kotlinx.serialization.serializer + +inline fun StateKeeper.register(key: String, noinline supplier: () -> T?) { + register(key = key, strategy = serializer(), supplier = supplier) +} + +inline fun StateKeeper.consume(key: String): T? = + consume(key = key, strategy = serializer()) diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt index eaf56b1d3..03ea6be9e 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt @@ -17,7 +17,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { fun GIVEN_children_created_with_active_child_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - children.getById(id = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -28,7 +28,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { fun GIVEN_children_created_with_active_child_and_disabled_callback_registered_WHEN_back_THEN_callback_not_called() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - children.getById(id = 3).requireInstance().backHandler.register(BackCallback(isEnabled = false) { isCalled = true }) + children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = false) { isCalled = true }) backDispatcher.back() @@ -39,7 +39,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { fun GIVEN_children_created_with_inactive_child_and_enabled_callback_registered_WHEN_back_THEN_callback_not_called() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - children.getById(id = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -50,7 +50,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { fun GIVEN_children_created_with_inactive_child_and_disabled_callback_registered_WHEN_back_THEN_callback_not_called() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - children.getById(id = 2).requireInstance().backHandler.register(BackCallback(isEnabled = false) { isCalled = true }) + children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = false) { isCalled = true }) backDispatcher.back() @@ -62,7 +62,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) navigate { listOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE) } - children.getById(id = 1).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + children.getByConfig(config = 1).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -74,7 +74,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) navigate { listOf(1 by ACTIVE, 2 by INACTIVE, 3 by ACTIVE) } - children.getById(id = 1).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + children.getByConfig(config = 1).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -86,7 +86,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) } - children.getById(id = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -98,7 +98,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } - children.getById(id = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -109,7 +109,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { fun GIVEN_enabled_callback_registered_in_inactive_child_and_switched_to_active_WHEN_back_THEN_callback_called() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - children.getById(id = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) } backDispatcher.back() @@ -121,7 +121,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { fun GIVEN_enabled_callback_registered_in_inactive_child_and_switched_to_destroyed_WHEN_back_THEN_callback_not_called() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - children.getById(id = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) } backDispatcher.back() @@ -133,7 +133,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { fun GIVEN_enabled_callback_registered_in_active_child_and_switched_to_inactive_WHEN_back_THEN_callback_not_called() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - children.getById(id = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } backDispatcher.back() @@ -145,7 +145,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { fun GIVEN_enabled_callback_registered_in_active_child_and_switched_to_destroyed_WHEN_back_THEN_callback_not_called() { var isCalled = false val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - children.getById(id = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } backDispatcher.back() diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt index acdda8e15..b8d0837eb 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt @@ -75,7 +75,7 @@ internal class ChildrenBasicTest : ChildrenTestBase() { val destroyEvents = ArrayList() context.children(initialState = stateOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE)) { config, componentContext -> - componentContext.lifecycle.doOnDestroy { destroyEvents += config.id } + componentContext.lifecycle.doOnDestroy { destroyEvents += config } Component(config = config, componentContext = componentContext) } @@ -89,7 +89,7 @@ internal class ChildrenBasicTest : ChildrenTestBase() { val createEvents = ArrayList() context.children(initialState = stateOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE)) { config, componentContext -> - createEvents += config.id + createEvents += config Component(config = config, componentContext = componentContext) } @@ -108,7 +108,7 @@ internal class ChildrenBasicTest : ChildrenTestBase() { val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) newContext.children { config, componentContext -> - createEvents += config.id + createEvents += config Component(config = config, componentContext = componentContext) } @@ -119,7 +119,7 @@ internal class ChildrenBasicTest : ChildrenTestBase() { fun GIVEN_active_child_with_retained_instance_WHEN_child_removed_THEN_component_destroyed_before_instance() { val destroyEvents = ArrayList() val children by context.children(initialState = stateOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE)) - val component = children.getById(id = 3).requireInstance() + val component = children.getByConfig(config = 3).requireInstance() val instance = component.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) component.lifecycle.doOnDestroy { destroyEvents += "component" } instance.onDestroyed = { destroyEvents += "instance" } @@ -133,7 +133,7 @@ internal class ChildrenBasicTest : ChildrenTestBase() { fun GIVEN_active_child_with_retained_instance_WHEN_child_switched_to_destroyed_THEN_component_destroyed_before_instance() { val destroyEvents = ArrayList() val children by context.children(initialState = stateOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE)) - val component = children.getById(id = 3).requireInstance() + val component = children.getByConfig(config = 3).requireInstance() val instance = component.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) component.lifecycle.doOnDestroy { destroyEvents += "component" } instance.onDestroyed = { destroyEvents += "instance" } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt index ffad78493..115e4ac36 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt @@ -24,8 +24,8 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { fun WHEN_created_THEN_lifecycles_correct() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - assertEquals(Lifecycle.State.CREATED, children.getById(id = 2).requireInstance().lifecycle.state) - assertEquals(Lifecycle.State.RESUMED, children.getById(id = 3).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 2).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.RESUMED, children.getByConfig(config = 3).requireInstance().lifecycle.state) } @Test @@ -47,15 +47,15 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { navigate { it + (2 by INACTIVE) } navigate { it + (3 by ACTIVE) } - assertEquals(Lifecycle.State.CREATED, children.getById(id = 2).requireInstance().lifecycle.state) - assertEquals(Lifecycle.State.RESUMED, children.getById(id = 3).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 2).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.RESUMED, children.getByConfig(config = 3).requireInstance().lifecycle.state) } @Test fun WHEN_destroyed_child_removed_THEN_state_updated() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - navigate { state -> state.filterNot { it.configuration.id == 1 } } + navigate { state -> state.filterNot { it.configuration == 1 } } children.assertChildren(2 to 2, 3 to 3) } @@ -64,7 +64,7 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { fun WHEN_inactive_child_removed_THEN_state_updated() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - navigate { state -> state.filterNot { it.configuration.id == 2 } } + navigate { state -> state.filterNot { it.configuration == 2 } } children.assertChildren(1 to null, 3 to 3) } @@ -72,9 +72,9 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { @Test fun WHEN_inactive_child_removed_THEN_lifecycle_destroyed() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val component = children.getById(id = 2).requireInstance() + val component = children.getByConfig(config = 2).requireInstance() - navigate { state -> state.filterNot { it.configuration.id == 2 } } + navigate { state -> state.filterNot { it.configuration == 2 } } assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state) } @@ -83,7 +83,7 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { fun WHEN_active_child_removed_THEN_state_updated() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - navigate { state -> state.filterNot { it.configuration.id == 3 } } + navigate { state -> state.filterNot { it.configuration == 3 } } children.assertChildren(1 to null, 2 to 2) } @@ -91,9 +91,9 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { @Test fun WHEN_active_child_removed_THEN_lifecycle_destroyed() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val component = children.getById(id = 3).requireInstance() + val component = children.getByConfig(config = 3).requireInstance() - navigate { state -> state.filterNot { it.configuration.id == 3 } } + navigate { state -> state.filterNot { it.configuration == 3 } } assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state) } @@ -110,8 +110,8 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { @Test fun WHEN_all_children_removed_THEN_lifecycles_destroyed() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val component2 = children.getById(id = 2).requireInstance() - val component3 = children.getById(id = 3).requireInstance() + val component2 = children.getByConfig(config = 2).requireInstance() + val component3 = children.getByConfig(config = 3).requireInstance() navigate { emptyList() } @@ -125,7 +125,7 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { navigate { listOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE) } - assertEquals(Lifecycle.State.CREATED, children.getById(id = 1).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 1).requireInstance().lifecycle.state) } @Test @@ -134,13 +134,13 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) } - assertEquals(Lifecycle.State.RESUMED, children.getById(id = 2).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.RESUMED, children.getByConfig(config = 2).requireInstance().lifecycle.state) } @Test fun WHEN_child_switched_from_inactive_to_destroyed_THEN_lifecycle_destroyed() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val component = children.getById(id = 2).requireInstance() + val component = children.getByConfig(config = 2).requireInstance() navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) } @@ -153,13 +153,13 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } - assertEquals(Lifecycle.State.CREATED, children.getById(id = 3).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 3).requireInstance().lifecycle.state) } @Test fun WHEN_child_switched_from_active_to_destroyed_THEN_lifecycle_destroyed() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val component = children.getById(id = 3).requireInstance() + val component = children.getByConfig(config = 3).requireInstance() navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } @@ -172,15 +172,15 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { lifecycle.stop() - assertEquals(Lifecycle.State.CREATED, children.getById(id = 2).requireInstance().lifecycle.state) - assertEquals(Lifecycle.State.CREATED, children.getById(id = 3).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 2).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 3).requireInstance().lifecycle.state) } @Test fun WHEN_context_lifecycle_destroyed_THEN_all_component_lifecycles_destroyed() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val component2 = children.getById(id = 2).requireInstance() - val component3 = children.getById(id = 3).requireInstance() + val component2 = children.getByConfig(config = 2).requireInstance() + val component3 = children.getByConfig(config = 3).requireInstance() lifecycle.destroy() diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt index e486b2976..9e0cea576 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt @@ -21,7 +21,7 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { @Test fun WHEN_child_switched_from_inactive_to_destroyed_THEN_instance_destroyed() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val component = children.getById(id = 2).requireInstance() + val component = children.getByConfig(config = 2).requireInstance() val instance = component.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) } @@ -32,7 +32,7 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { @Test fun WHEN_child_switched_from_active_to_destroyed_THEN_instance_destroyed() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val component = children.getById(id = 3).requireInstance() + val component = children.getByConfig(config = 3).requireInstance() val instance = component.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } @@ -43,7 +43,7 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { @Test fun WHEN_child_switched_from_active_to_inactive_THEN_instance_not_destroyed() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val instance = children.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val instance = children.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } @@ -53,11 +53,11 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { @Test fun GIVEN_child_switched_from_inactive_to_destroyed_WHEN_child_switched_to_inactive_THEN_instance_not_retained() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val oldInstance = children.getById(id = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val oldInstance = children.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) } navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) } - val newInstance = children.getById(id = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val newInstance = children.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) assertNotSame(oldInstance, newInstance) } @@ -65,11 +65,11 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { @Test fun GIVEN_child_switched_from_active_to_destroyed_WHEN_child_switched_to_active_THEN_instance_not_retained() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val oldInstance = children.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val oldInstance = children.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) } - val newInstance = children.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val newInstance = children.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) assertNotSame(oldInstance, newInstance) } @@ -80,13 +80,13 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val oldInstance = oldChildren.getById(id = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val oldInstance = oldChildren.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) val newChildren by newContext.children() - val newInstance = newChildren.getById(id = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val newInstance = newChildren.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) assertSame(oldInstance, newInstance) } @@ -97,7 +97,7 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val instance = oldChildren.getById(id = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val instance = oldChildren.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) @@ -113,13 +113,13 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val oldInstance = oldChildren.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val oldInstance = oldChildren.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) val newChildren by newContext.children() - val newInstance = newChildren.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val newInstance = newChildren.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) assertSame(oldInstance, newInstance) } @@ -130,7 +130,7 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val instance = oldChildren.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val instance = oldChildren.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) @@ -146,7 +146,7 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val instance = oldChildren.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + val instance = oldChildren.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt index 1e26018fd..a462f402d 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt @@ -1,14 +1,13 @@ package com.arkivanov.decompose.router.children import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.consume +import com.arkivanov.decompose.register import com.arkivanov.decompose.router.children.ChildNavState.Status.ACTIVE import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED import com.arkivanov.decompose.router.children.ChildNavState.Status.INACTIVE -import com.arkivanov.decompose.statekeeper.ParcelableStub import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher import com.arkivanov.decompose.value.getValue -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.statekeeper.consume import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -22,9 +21,9 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { fun WHEN_child_switched_from_active_to_inactive_THEN_state_not_saved() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) var isCalled = false - children.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { + children.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { isCalled = true - ParcelableStub() + "SavedState" } navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } @@ -36,9 +35,9 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { fun WHEN_child_switched_from_active_to_destroyed_THEN_state_saved() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) var isCalled = false - children.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { + children.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { isCalled = true - ParcelableStub() + "SavedState" } navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } @@ -50,9 +49,9 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { fun WHEN_child_switched_from_inactive_to_destroyed_THEN_state_saved() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) var isCalled = false - children.getById(id = 2).requireInstance().stateKeeper.register(key = "key") { + children.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { isCalled = true - ParcelableStub() + "SavedState" } navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) } @@ -64,9 +63,9 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { fun WHEN_child_switched_from_inactive_to_active_THEN_state_not_saved() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) var isCalled = false - children.getById(id = 2).requireInstance().stateKeeper.register(key = "key") { + children.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { isCalled = true - ParcelableStub() + "SavedState" } navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) } @@ -79,10 +78,10 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { var isCalled = false context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) { config, componentContext -> val component = Component(config, componentContext) - if (config.id == 1) { + if (config == 1) { component.stateKeeper.register(key = "key") { isCalled = true - ParcelableStub() + "SavedState" } } component @@ -98,10 +97,10 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { var isCalled = false context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) { config, componentContext -> val component = Component(config, componentContext) - if (config.id == 1) { + if (config == 1) { component.stateKeeper.register(key = "key") { isCalled = true - ParcelableStub() + "SavedState" } } component @@ -115,25 +114,25 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { @Test fun GIVEN_child_switched_from_active_to_destroyed_WHEN_child_switched_to_active_THEN_state_restored() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - children.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) } + children.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) } - val restoredState = children.getById(id = 3).requireInstance().stateKeeper.consume(key = "key") + val restoredState = children.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") - assertEquals(Config(id = 30), restoredState) + assertEquals(30, restoredState) } @Test fun GIVEN_child_switched_from_active_to_destroyed_WHEN_child_switched_to_inactive_THEN_state_restored() { val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - children.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) } + children.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } - val restoredState = children.getById(id = 3).requireInstance().stateKeeper.consume(key = "key") + val restoredState = children.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") - assertEquals(Config(id = 30), restoredState) + assertEquals(30, restoredState) } @Test @@ -183,18 +182,18 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - oldChildren.getById(id = 2).requireInstance().stateKeeper.register(key = "key") { Config(id = 20) } - oldChildren.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) } + oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } + oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) val newChildren by newContext.children() - val restoredState2 = newChildren.getById(id = 2).requireInstance().stateKeeper.consume(key = "key") - val restoredState3 = newChildren.getById(id = 3).requireInstance().stateKeeper.consume(key = "key") + val restoredState2 = newChildren.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") + val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") - assertEquals(Config(id = 20), restoredState2) - assertEquals(Config(id = 30), restoredState3) + assertEquals(20, restoredState2) + assertEquals(30, restoredState3) } @Test @@ -202,15 +201,15 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), saveState = { null }) - oldChildren.getById(id = 2).requireInstance().stateKeeper.register(key = "key") { Config(id = 20) } - oldChildren.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) } + oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } + oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) val newChildren by newContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val restoredState2 = newChildren.getById(id = 2).requireInstance().stateKeeper.consume(key = "key") - val restoredState3 = newChildren.getById(id = 3).requireInstance().stateKeeper.consume(key = "key") + val restoredState2 = newChildren.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") + val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") assertNull(restoredState2) assertNull(restoredState3) @@ -221,15 +220,15 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - oldChildren.getById(id = 2).requireInstance().stateKeeper.register(key = "key") { Config(id = 20) } - oldChildren.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) } + oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } + oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) val newChildren by newContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), restoreState = { null }) - val restoredState2 = newChildren.getById(id = 2).requireInstance().stateKeeper.consume(key = "key") - val restoredState3 = newChildren.getById(id = 3).requireInstance().stateKeeper.consume(key = "key") + val restoredState2 = newChildren.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") + val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") assertNull(restoredState2) assertNull(restoredState3) @@ -240,16 +239,16 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - oldChildren.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) } + oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }) navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } - val restoredState3 = newChildren.getById(id = 3).requireInstance().stateKeeper.consume(key = "key") + val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") - assertEquals(Config(id = 30), restoredState3) + assertEquals(30, restoredState3) } @Test @@ -257,16 +256,16 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - oldChildren.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) } + oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }) navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) } - val restoredState3 = newChildren.getById(id = 3).requireInstance().stateKeeper.consume(key = "key") + val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") - assertEquals(Config(id = 30), restoredState3) + assertEquals(30, restoredState3) } @Test @@ -274,22 +273,22 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE)) - oldChildren.getById(id = 2).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) } + oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 30 } navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) } val savedState1 = oldStateKeeper.save() val newStateKeeper1 = TestStateKeeperDispatcher(savedState1) val newContext1 = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper1) val newChildren1 by newContext1.children(restoreState = { stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) }) - newChildren1.getById(id = 2).requireInstance().stateKeeper.register(key = "key") { Config(id = 31) } + newChildren1.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 31 } val savedState2 = newStateKeeper1.save() val newStateKeeper2 = TestStateKeeperDispatcher(savedState2) val newContext2 = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper2) val newChildren2 by newContext2.children(restoreState = { stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) }) - val restoredState2 = newChildren2.getById(id = 2).requireInstance().stateKeeper.consume(key = "key") + val restoredState2 = newChildren2.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") - assertEquals(Config(id = 31), restoredState2) + assertEquals(31, restoredState2) } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt index 6f52ac11c..6c5e26c1d 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt @@ -9,10 +9,9 @@ import com.arkivanov.essenty.backhandler.BackDispatcher import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.resume -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.ParcelableContainer -import com.arkivanov.essenty.parcelable.Parcelize -import com.arkivanov.essenty.parcelable.consumeRequired +import com.arkivanov.essenty.statekeeper.SerializableContainer +import com.arkivanov.essenty.statekeeper.consumeRequired +import kotlinx.serialization.Serializable import kotlin.test.BeforeTest import kotlin.test.assertContentEquals @@ -37,16 +36,17 @@ internal open class ChildrenTestBase { protected fun ComponentContext.children( initialState: TestNavState = TestNavState(), - saveState: (state: TestNavState) -> ParcelableContainer? = { state -> - ParcelableContainer( - SavedNavState( + saveState: (state: TestNavState) -> SerializableContainer? = { state -> + SerializableContainer( + value = SavedNavState( configurations = state.children.map { it.configuration }, statuses = state.children.map { it.status }, - ) + ), + strategy = SavedNavState.serializer(), ) }, - restoreState: (container: ParcelableContainer) -> TestNavState? = { container -> - val savedState = container.consumeRequired() + restoreState: (container: SerializableContainer) -> TestNavState? = { container -> + val savedState = container.consumeRequired(SavedNavState.serializer()) TestNavState( children = savedState.configurations.zip(savedState.statuses).map { (configuration, status) -> SimpleChildNavState( @@ -63,8 +63,8 @@ internal open class ChildrenTestBase { oldState: TestNavState, ) -> Unit = { _, _, _ -> }, backTransformer: (state: TestNavState) -> (() -> TestNavState)? = { null }, - childFactory: (Config, ComponentContext) -> Component = ::Component, - ): Value>> = + childFactory: (Int, ComponentContext) -> Component = ::Component, + ): Value>> = children( source = navigation, key = "Key", @@ -79,53 +79,46 @@ internal open class ChildrenTestBase { childFactory = childFactory, ) - protected fun navigate(transformer: (List>) -> List>) { + protected fun navigate(transformer: (List>) -> List>) { navigation.navigate { it.copy(children = transformer(it.children)) } } - protected infix fun Int.by(status: ChildNavState.Status): SimpleChildNavState = - SimpleChildNavState(configuration = Config(id = this), status = status) + protected infix fun Int.by(status: ChildNavState.Status): SimpleChildNavState = + SimpleChildNavState(configuration = this, status = status) - protected fun stateOf(vararg children: SimpleChildNavState): TestNavState = + protected fun stateOf(vararg children: SimpleChildNavState): TestNavState = TestNavState(children = children.asList()) - protected fun List>.assertChildren(vararg children: Pair) { + protected fun List>.assertChildren(vararg children: Pair) { assertContentEquals( children.toList(), map { child -> when (child) { - is Child.Created -> child.configuration.id to child.instance.id - is Child.Destroyed -> child.configuration.id to null + is Child.Created -> child.configuration to child.instance.config + is Child.Destroyed -> child.configuration to null } } ) } - protected fun List>.getById(id: Int): Child = - first { it.configuration.id == id } + protected fun List>.getByConfig(config: Int): Child = + first { it.configuration == config } protected fun Child<*, Component>.requireInstance(): Component = requireNotNull(instance) protected data class TestNavState( - override val children: List> = emptyList(), - ) : NavState - - @Parcelize - protected data class Config( - val id: Int, - ) : Parcelable + override val children: List> = emptyList(), + ) : NavState protected class Component( - config: Config, + val config: Int, componentContext: ComponentContext, - ) : ComponentContext by componentContext { - val id: Int = config.id - } + ) : ComponentContext by componentContext - @Parcelize + @Serializable protected class SavedNavState( - val configurations: List, + val configurations: List, val statuses: List, - ) : Parcelable + ) } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/BaseChildPagesTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/BaseChildPagesTest.kt index fa2b29d10..85a3ea9f4 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/BaseChildPagesTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/BaseChildPagesTest.kt @@ -4,8 +4,7 @@ import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.Value import com.arkivanov.essenty.lifecycle.Lifecycle -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.Parcelize +import kotlinx.serialization.builtins.serializer import kotlin.test.assertContentEquals import kotlin.test.assertEquals import kotlin.test.assertIs @@ -13,69 +12,59 @@ import kotlin.test.assertTrue abstract class BaseChildPagesTest { - protected val navigation: PagesNavigation = PagesNavigation() + protected val navigation: PagesNavigation = PagesNavigation() protected fun ComponentContext.childPages( - initialPages: Pages = Pages(), + initialPages: Pages = Pages(), persistent: Boolean = true, - ): Value> = + ): Value> = childPages( source = navigation, + serializer = Int.serializer().takeIf { persistent }, initialPages = { initialPages }, - persistent = persistent, handleBackButton = true, childFactory = ::Component, ) - protected fun configs(vararg ids: Int): List = - ids.map { Config(id = it) } - - protected fun ChildPages.assertPages(selectedIndex: Int, size: Int) { + protected fun ChildPages.assertPages(selectedIndex: Int, size: Int) { assertPages( ids = List(size) { it }, selectedIndex = selectedIndex, ) } - protected fun ChildPages.assertPages(ids: List, selectedIndex: Int) { + protected fun ChildPages.assertPages(ids: List, selectedIndex: Int) { val pages = ids.mapIndexed { index, id -> id to id.takeIf { index in (selectedIndex - 1)..(selectedIndex + 1) } } - assertContentEquals(pages, items.map { it.configuration.id to it.instance?.id }) + assertContentEquals(pages, items.map { it.configuration to it.instance?.config }) assertEquals(selectedIndex, selectedIndex) items.forEachIndexed { index, item -> when (index) { selectedIndex -> { - assertIs>(item) + assertIs>(item) assertEquals(Lifecycle.State.RESUMED, item.instance.lifecycle.state) } in (selectedIndex - 1)..(selectedIndex + 1) -> { - assertIs>(item) + assertIs>(item) assertEquals(Lifecycle.State.CREATED, item.instance.lifecycle.state) } - else -> assertIs>(item, "Index: $index") + else -> assertIs>(item, "Index: $index") } } } - protected fun ChildPages.assertPagesEmpty() { + protected fun ChildPages.assertPagesEmpty() { assertTrue(items.isEmpty()) } - @Parcelize - protected data class Config( - val id: Int, - ) : Parcelable - protected class Component( - config: Config, + val config: Int, componentContext: ComponentContext, - ) : ComponentContext by componentContext { - val id: Int = config.id - } + ) : ComponentContext by componentContext } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesBackButtonTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesBackButtonTest.kt index 5ec925df2..ddf6a8629 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesBackButtonTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesBackButtonTest.kt @@ -24,14 +24,14 @@ class ChildPagesBackButtonTest : BaseChildPagesTest() { @Test fun WHEN_5_pages_and_selected_1_THEN_isEnabled_true() { - context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 1)) + context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 1)) assertTrue(backDispatcher.isEnabled) } @Test fun GIVEN_5_pages_and_selected_1_WHEN_back_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 1)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 1)) backDispatcher.back() @@ -40,7 +40,7 @@ class ChildPagesBackButtonTest : BaseChildPagesTest() { @Test fun WHEN_5_pages_and_selected_0_THEN_isEnabled_false() { - context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) assertFalse(backDispatcher.isEnabled) } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesClearTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesClearTest.kt index 7cc3e3896..02f1424f7 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesClearTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesClearTest.kt @@ -29,7 +29,7 @@ class ChildPagesClearTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_0_WHEN_clear_THEN_pages_empty() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) navigation.clear() @@ -38,7 +38,7 @@ class ChildPagesClearTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_2_WHEN_clear_THEN_pages_empty() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 2)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 2)) navigation.clear() @@ -47,7 +47,7 @@ class ChildPagesClearTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_4_WHEN_clear_THEN_pages_empty() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 4)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 4)) navigation.clear() diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesIntegrationTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesIntegrationTest.kt index 186cac6c3..0fcaaf904 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesIntegrationTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesIntegrationTest.kt @@ -9,8 +9,7 @@ import com.arkivanov.essenty.backhandler.BackDispatcher import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.resume -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.Parcelize +import kotlinx.serialization.builtins.serializer import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -18,7 +17,7 @@ import kotlin.test.assertEquals @Suppress("TestFunctionName") class ChildPagesIntegrationTest { - private val navigation = PagesNavigation() + private val navigation = PagesNavigation() private val lifecycle = LifecycleRegistry() private val stateKeeper = TestStateKeeperDispatcher() private val instanceKeeper = InstanceKeeperDispatcher() @@ -39,7 +38,7 @@ class ChildPagesIntegrationTest { @Test fun GIVEN_three_pages_and_last_selected_WHEN_selectNext_circular_THEN_first_selected() { - val pages by context.childPages(initialPages = Pages(items = configs(1, 2, 3), selectedIndex = 2)) + val pages by context.childPages(initialPages = Pages(items = listOf(1, 2, 3), selectedIndex = 2)) navigation.selectNext(circular = true) @@ -48,7 +47,7 @@ class ChildPagesIntegrationTest { @Test fun GIVEN_three_pages_and_first_selected_WHEN_selectNext_circular_THEN_first_selected() { - val pages by context.childPages(initialPages = Pages(items = configs(1, 2, 3), selectedIndex = 2)) + val pages by context.childPages(initialPages = Pages(items = listOf(1, 2, 3), selectedIndex = 2)) navigation.selectNext(circular = true) @@ -56,29 +55,14 @@ class ChildPagesIntegrationTest { } private fun ComponentContext.childPages( - initialPages: Pages = Pages(), + initialPages: Pages = Pages(), persistent: Boolean = true, - ): Value> = + ): Value> = childPages( source = navigation, + serializer = Int.serializer().takeIf { persistent }, initialPages = { initialPages }, - persistent = persistent, handleBackButton = true, - childFactory = ::Component, + childFactory = { config, _ -> config }, ) - - private fun configs(vararg ids: Int): List = - ids.map { Config(id = it) } - - @Parcelize - private data class Config( - val id: Int, - ) : Parcelable - - private class Component( - config: Config, - componentContext: ComponentContext, - ) : ComponentContext by componentContext { - val id: Int = config.id - } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSavedStateTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSavedStateTest.kt index 1c09fc07c..36484665f 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSavedStateTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSavedStateTest.kt @@ -23,7 +23,7 @@ class ChildPagesSavedStateTest : BaseChildPagesTest() { var stateKeeper = StateKeeperDispatcher() var context = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = stateKeeper) context.childPages( - initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 2), + initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 2), persistent = true, ) @@ -40,14 +40,14 @@ class ChildPagesSavedStateTest : BaseChildPagesTest() { var stateKeeper = StateKeeperDispatcher() var context = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = stateKeeper) context.childPages( - initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 2), + initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 2), persistent = false, ) val savedState = stateKeeper.save() stateKeeper = StateKeeperDispatcher(savedState = savedState) context = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = stateKeeper) - val pages2 by context.childPages(initialPages = Pages(items = configs(5, 6, 7), selectedIndex = 0)) + val pages2 by context.childPages(initialPages = Pages(items = listOf(5, 6, 7), selectedIndex = 0)) pages2.assertPages(ids = listOf(5, 6, 7), selectedIndex = 0) } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectFirstTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectFirstTest.kt index dda052dd4..811447478 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectFirstTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectFirstTest.kt @@ -29,7 +29,7 @@ class ChildPagesSelectFirstTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_0_WHEN_selectFirst_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) navigation.selectFirst() @@ -38,7 +38,7 @@ class ChildPagesSelectFirstTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_2_WHEN_selectFirst_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 2)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 2)) navigation.selectFirst() @@ -47,7 +47,7 @@ class ChildPagesSelectFirstTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_4_WHEN_selectFirst_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 4)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 4)) navigation.selectFirst() diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectLastTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectLastTest.kt index ec515ed6c..5c4d74de5 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectLastTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectLastTest.kt @@ -29,7 +29,7 @@ class ChildPagesSelectLastTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_0_WHEN_selectLast_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) navigation.selectLast() @@ -38,7 +38,7 @@ class ChildPagesSelectLastTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_2_WHEN_selectLast_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 2)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 2)) navigation.selectLast() @@ -47,7 +47,7 @@ class ChildPagesSelectLastTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_4_WHEN_selectLast_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 4)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 4)) navigation.selectLast() diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectNextTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectNextTest.kt index 03369365f..8c2aec0be 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectNextTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectNextTest.kt @@ -29,7 +29,7 @@ class ChildPagesSelectNextTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_0_WHEN_selectNext_THEN_selected_1() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) navigation.selectNext(circular = false) @@ -38,7 +38,7 @@ class ChildPagesSelectNextTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_0_WHEN_selectNext_circular_THEN_selected_1() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) navigation.selectNext(circular = true) @@ -47,7 +47,7 @@ class ChildPagesSelectNextTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_1_WHEN_selectNext_THEN_selected_2() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 1)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 1)) navigation.selectNext(circular = false) @@ -56,7 +56,7 @@ class ChildPagesSelectNextTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_1_WHEN_selectNext_circular_THEN_selected_2() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 1)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 1)) navigation.selectNext(circular = true) @@ -65,7 +65,7 @@ class ChildPagesSelectNextTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_4_WHEN_selectNext_THEN_selected_4() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 4)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 4)) navigation.selectNext(circular = false) @@ -74,7 +74,7 @@ class ChildPagesSelectNextTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_4_WHEN_selectNext_circular_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 4)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 4)) navigation.selectNext(circular = true) diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectPrevTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectPrevTest.kt index 3a4396222..e98300062 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectPrevTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectPrevTest.kt @@ -29,7 +29,7 @@ class ChildPagesSelectPrevTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_4_WHEN_selectPrev_THEN_selected_3() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 4)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 4)) navigation.selectPrev(circular = false) @@ -38,7 +38,7 @@ class ChildPagesSelectPrevTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_4_WHEN_selectPrev_circular_THEN_selected_3() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 4)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 4)) navigation.selectPrev(circular = true) @@ -47,7 +47,7 @@ class ChildPagesSelectPrevTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_3_WHEN_selectPrev_THEN_selected_2() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 3)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 3)) navigation.selectPrev(circular = false) @@ -56,7 +56,7 @@ class ChildPagesSelectPrevTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_3_WHEN_selectPrev_circular_THEN_selected_2() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 3)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 3)) navigation.selectPrev(circular = true) @@ -65,7 +65,7 @@ class ChildPagesSelectPrevTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_0_WHEN_selectPrev_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) navigation.selectPrev(circular = false) @@ -74,7 +74,7 @@ class ChildPagesSelectPrevTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_0_WHEN_selectPrev_circular_THEN_selected_4() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) navigation.selectPrev(circular = true) diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectTest.kt index d02f33023..5777d7e10 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSelectTest.kt @@ -29,7 +29,7 @@ class ChildPagesSelectTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_0_WHEN_select_0_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) navigation.select(index = 0) @@ -38,7 +38,7 @@ class ChildPagesSelectTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_0_WHEN_select_2_THEN_selected_2() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) navigation.select(index = 2) @@ -47,7 +47,7 @@ class ChildPagesSelectTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_0_WHEN_select_4_THEN_selected_4() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 0)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 0)) navigation.select(index = 4) @@ -56,7 +56,7 @@ class ChildPagesSelectTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_4_WHEN_select_0_THEN_selected_0() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 4)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 4)) navigation.select(index = 0) @@ -65,7 +65,7 @@ class ChildPagesSelectTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_4_WHEN_select_2_THEN_selected_2() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 4)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 4)) navigation.select(index = 2) @@ -74,7 +74,7 @@ class ChildPagesSelectTest : BaseChildPagesTest() { @Test fun GIVEN_5_pages_and_selected_4_WHEN_select_4_THEN_selected_4() { - val pages by context.childPages(initialPages = Pages(items = configs(0, 1, 2, 3, 4), selectedIndex = 4)) + val pages by context.childPages(initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 4)) navigation.select(index = 4) diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/slot/ChildSlotIntegrationTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/slot/ChildSlotIntegrationTest.kt index ab288b4e0..ea12e3419 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/slot/ChildSlotIntegrationTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/slot/ChildSlotIntegrationTest.kt @@ -3,6 +3,8 @@ package com.arkivanov.decompose.router.slot import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.consume +import com.arkivanov.decompose.register import com.arkivanov.decompose.router.TestInstance import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher import com.arkivanov.decompose.value.Value @@ -13,9 +15,7 @@ import com.arkivanov.essenty.instancekeeper.getOrCreate import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.resume -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.Parcelize -import com.arkivanov.essenty.statekeeper.consume +import kotlinx.serialization.builtins.serializer import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -28,7 +28,7 @@ import kotlin.test.assertTrue @Suppress("TestFunctionName") class ChildSlotIntegrationTest { - private val navigation = SlotNavigation() + private val navigation = SlotNavigation() private val lifecycle = LifecycleRegistry() private val stateKeeperDispatcher = TestStateKeeperDispatcher() private val instanceKeeperDispatcher = InstanceKeeperDispatcher() @@ -56,7 +56,7 @@ class ChildSlotIntegrationTest { @Test fun WHEN_created_with_configuration_THEN_slot_active() { - val slot by context.childSlot(initialConfiguration = Config(1)) + val slot by context.childSlot(initialConfiguration = 1) slot.assertChild(1) } @@ -65,14 +65,14 @@ class ChildSlotIntegrationTest { fun GIVEN_not_active_WHEN_activate_THEN_slot_active() { val slot by context.childSlot(initialConfiguration = null) - navigation.activate(Config(1)) + navigation.activate(1) slot.assertChild(1) } @Test fun GIVEN_active_WHEN_dismiss_THEN_slot_not_active() { - val slot by context.childSlot(initialConfiguration = Config(1)) + val slot by context.childSlot(initialConfiguration = 1) navigation.dismiss() @@ -81,28 +81,28 @@ class ChildSlotIntegrationTest { @Test fun GIVEN_active_WHEN_activate_with_same_configuration_THEN_same_slot_active() { - val slot by context.childSlot(initialConfiguration = Config(1)) + val slot by context.childSlot(initialConfiguration = 1) - navigation.activate(Config(1)) + navigation.activate(1) slot.assertChild(1) } @Test fun GIVEN_active_WHEN_activate_with_same_configuration_THEN_same_instance_active() { - val slot by context.childSlot(initialConfiguration = Config(1)) + val slot by context.childSlot(initialConfiguration = 1) val instance = slot.requireChild().instance - navigation.activate(Config(1)) + navigation.activate(1) assertSame(instance, slot.child?.instance) } @Test fun GIVEN_active_WHEN_activate_with_other_configuration_THEN_other_slot_active() { - val slot by context.childSlot(initialConfiguration = Config(1)) + val slot by context.childSlot(initialConfiguration = 1) - navigation.activate(Config(2)) + navigation.activate(2) slot.assertChild(2) } @@ -111,14 +111,14 @@ class ChildSlotIntegrationTest { fun GIVEN_not_active_WHEN_activate_THEN_lifecycle_resumed() { val slot by context.childSlot(initialConfiguration = null) - navigation.activate(Config(1)) + navigation.activate(1) assertEquals(Lifecycle.State.RESUMED, slot.requireChild().instance.lifecycle.state) } @Test fun GIVEN_active_WHEN_dismiss_THEN_lifecycle_destroyed() { - val slot by context.childSlot(initialConfiguration = Config(1)) + val slot by context.childSlot(initialConfiguration = 1) val lifecycle = slot.requireChild().instance.lifecycle navigation.dismiss() @@ -135,14 +135,14 @@ class ChildSlotIntegrationTest { @Test fun GIVEN_active_WHEN_parent_backDispatcher_isEnabled_THEN_true() { - context.childSlot(initialConfiguration = Config(1)) + context.childSlot(initialConfiguration = 1) assertTrue(backDispatcher.isEnabled) } @Test fun GIVEN_active_WHEN_back_pressed_THEN_slot_not_active() { - val slot by context.childSlot(initialConfiguration = Config(1)) + val slot by context.childSlot(initialConfiguration = 1) backDispatcher.back() @@ -151,7 +151,7 @@ class ChildSlotIntegrationTest { @Test fun GIVEN_dismissed_via_back_pressed_WHEN_parent_backDispatcher_isEnabled_THEN_false() { - context.childSlot(initialConfiguration = Config(1)) + context.childSlot(initialConfiguration = 1) backDispatcher.back() assertFalse(backDispatcher.isEnabled) @@ -161,7 +161,7 @@ class ChildSlotIntegrationTest { fun GIVEN_persistent_WHEN_recreated_THEN_slot_active() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - oldContext.childSlot(initialConfiguration = Config(1), persistent = true) + oldContext.childSlot(initialConfiguration = 1, persistent = true) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) @@ -175,23 +175,23 @@ class ChildSlotIntegrationTest { fun GIVEN_persistent_WHEN_recreated_THEN_slot_state_restored() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - val slot by oldContext.childSlot(initialConfiguration = Config(1), persistent = true) - slot.requireChild().instance.stateKeeper.register("key") { Config(10) } + val slot by oldContext.childSlot(initialConfiguration = 1, persistent = true) + slot.requireChild().instance.stateKeeper.register("key") { 10 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) val newSlot by newContext.childSlot(initialConfiguration = null, persistent = true) - val restoredState = newSlot.requireChild().instance.stateKeeper.consume("key") + val restoredState = newSlot.requireChild().instance.stateKeeper.consume("key") - assertEquals(Config(10), restoredState) + assertEquals(10, restoredState) } @Test fun GIVEN_not_persistent_WHEN_recreated_THEN_slot_not_active() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - oldContext.childSlot(initialConfiguration = Config(1), persistent = false) + oldContext.childSlot(initialConfiguration = 1, persistent = false) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) @@ -203,7 +203,7 @@ class ChildSlotIntegrationTest { @Test fun WHEN_created_persistent_THEN_registered_in_parent_StateKeeper() { - context.childSlot(initialConfiguration = Config(1), persistent = true) + context.childSlot(initialConfiguration = 1, persistent = true) stateKeeperDispatcher.assertSupplierRegistered(key = "key") } @@ -213,7 +213,7 @@ class ChildSlotIntegrationTest { val oldStateKeeper = TestStateKeeperDispatcher() val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldSlot by oldContext.childSlot(initialConfiguration = Config(1), persistent = true) + val oldSlot by oldContext.childSlot(initialConfiguration = 1, persistent = true) val oldInstance = oldSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance) val savedState = oldStateKeeper.save() @@ -230,7 +230,7 @@ class ChildSlotIntegrationTest { val oldStateKeeper = TestStateKeeperDispatcher() val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldSlot by oldContext.childSlot(initialConfiguration = Config(1), persistent = true) + val oldSlot by oldContext.childSlot(initialConfiguration = 1, persistent = true) val instance = oldSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance) val savedState = oldStateKeeper.save() @@ -243,7 +243,7 @@ class ChildSlotIntegrationTest { @Test fun WHEN_created_persistent_THEN_registered_in_parent_InstanceKeeper() { - context.childSlot(initialConfiguration = Config(1), persistent = true) + context.childSlot(initialConfiguration = 1, persistent = true) assertNotNull(instanceKeeperDispatcher.get(key = "key")) } @@ -253,13 +253,13 @@ class ChildSlotIntegrationTest { val oldStateKeeper = TestStateKeeperDispatcher() val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldSlot by oldContext.childSlot(initialConfiguration = Config(1), persistent = false) + val oldSlot by oldContext.childSlot(initialConfiguration = 1, persistent = false) val oldInstance = oldSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) - val newSlot by newContext.childSlot(initialConfiguration = Config(1), persistent = false) + val newSlot by newContext.childSlot(initialConfiguration = 1, persistent = false) val newInstance = newSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance) assertNotSame(oldInstance, newInstance) @@ -270,7 +270,7 @@ class ChildSlotIntegrationTest { val oldStateKeeper = TestStateKeeperDispatcher() val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldSlot by oldContext.childSlot(initialConfiguration = Config(1), persistent = false) + val oldSlot by oldContext.childSlot(initialConfiguration = 1, persistent = false) val instance = oldSlot.requireChild().instance.instanceKeeper.getOrCreate(::TestInstance) val savedState = oldStateKeeper.save() @@ -282,35 +282,28 @@ class ChildSlotIntegrationTest { } private fun ComponentContext.childSlot( - initialConfiguration: Config?, + initialConfiguration: Int?, persistent: Boolean = true, - ): Value> = + ): Value> = childSlot( source = navigation, + serializer = Int.serializer().takeIf { persistent }, key = "key", initialConfiguration = { initialConfiguration }, handleBackButton = true, - persistent = persistent, childFactory = ::Component, ) - private fun ChildSlot.requireChild(): Child.Created = + private fun ChildSlot.requireChild(): Child.Created = requireNotNull(child) - private fun ChildSlot.assertChild(id: Int?) { - assertEquals(id, child?.configuration?.id) - assertEquals(id, child?.instance?.id) + private fun ChildSlot.assertChild(id: Int?) { + assertEquals(id, child?.configuration) + assertEquals(id, child?.instance?.config) } - @Parcelize - private data class Config( - val id: Int, - ) : Parcelable - private class Component( - config: Config, + val config: Int, componentContext: ComponentContext, - ) : ComponentContext by componentContext { - val id: Int = config.id - } + ) : ComponentContext by componentContext } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackIntegrationTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackIntegrationTest.kt index 5d9367d2b..70fb296cc 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackIntegrationTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackIntegrationTest.kt @@ -1,8 +1,9 @@ package com.arkivanov.decompose.router.stack -import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.consume +import com.arkivanov.decompose.register import com.arkivanov.decompose.router.TestInstance import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher import com.arkivanov.decompose.value.Value @@ -14,9 +15,7 @@ import com.arkivanov.essenty.instancekeeper.getOrCreate import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.resume -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.Parcelize -import com.arkivanov.essenty.statekeeper.consume +import kotlinx.serialization.Serializable import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertContentEquals @@ -259,16 +258,16 @@ class ChildStackIntegrationTest { val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) val oldStack by oldContext.childStack(initialStack = listOf(Config(1), Config(2), Config(3)), persistent = true) oldStack.items.forEach { (configuration, instance) -> - instance.stateKeeper.register(key = "key") { Config(configuration.id) } + instance.stateKeeper.register(key = "key") { configuration.id } } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) val newStack by newContext.childStack() - val restoredStates = newStack.items.map { it.instance.stateKeeper.consume(key = "key") } + val restoredStates = newStack.items.map { it.instance.stateKeeper.consume(key = "key") } - assertContentEquals(listOf(Config(1), Config(2), Config(3)), restoredStates) + assertContentEquals(listOf(1, 2, 3), restoredStates) } @Test @@ -277,22 +276,22 @@ class ChildStackIntegrationTest { val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) val oldStack by oldContext.childStack(initialStack = listOf(Config(1), Config(2), Config(3)), persistent = true) oldStack.items.forEach { (configuration, instance) -> - instance.stateKeeper.register(key = "key") { Config(configuration.id) } + instance.stateKeeper.register(key = "key") { configuration.id } } val savedState1 = oldStateKeeper.save() val newStateKeeper1 = TestStateKeeperDispatcher(savedState1) val newContext1 = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper1) val newStack1 by newContext1.childStack() - newStack1.items.forEach { it.instance.stateKeeper.consume(key = "key") } + newStack1.items.forEach { it.instance.stateKeeper.consume(key = "key") } val savedState2 = oldStateKeeper.save() val newStateKeeper2 = TestStateKeeperDispatcher(savedState2) val newContext2 = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper2) val newStack2 by newContext2.childStack() - val restoredStates = newStack2.items.map { it.instance.stateKeeper.consume(key = "key") } + val restoredStates = newStack2.items.map { it.instance.stateKeeper.consume(key = "key") } - assertContentEquals(listOf(Config(1), Config(2), Config(3)), restoredStates) + assertContentEquals(listOf(1, 2, 3), restoredStates) } @Test @@ -301,14 +300,14 @@ class ChildStackIntegrationTest { val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) val oldStack by oldContext.childStack(initialStack = listOf(Config(1), Config(2), Config(3)), persistent = false) oldStack.items.forEach { (configuration, instance) -> - instance.stateKeeper.register(key = "key") { Config(configuration.id) } + instance.stateKeeper.register(key = "key") { configuration.id } } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) val newStack by newContext.childStack(initialStack = listOf(Config(1), Config(2), Config(3))) - val restoredStates = newStack.items.map { it.instance.stateKeeper.consume(key = "key") } + val restoredStates = newStack.items.map { it.instance.stateKeeper.consume(key = "key") } assertContentEquals(listOf(null, null, null), restoredStates) } @@ -418,8 +417,8 @@ class ChildStackIntegrationTest { ): Value> = childStack( source = navigation, + serializer = Config.serializer().takeIf { persistent }, initialStack = { initialStack }, - persistent = persistent, handleBackButton = true, childFactory = ::Component, ) @@ -427,9 +426,6 @@ class ChildStackIntegrationTest { private val ChildStack.children: List> get() = items.map { child -> child.configuration.id to child.instance.id } - private fun ChildStack.getById(id: Int): Child.Created = - items.first { it.configuration.id == id } - private fun ChildStack.assertStack(vararg children: Pair) { assertEquals(children.toList(), this.children) assertEquals(Lifecycle.State.RESUMED, active.instance.lifecycle.state) @@ -440,17 +436,17 @@ class ChildStackIntegrationTest { ) } - @Parcelize + @Serializable private data class Config( val id: Int, val routingParams: RoutingParams? = null, - ) : Parcelable + ) - @Parcelize + @Serializable private data class RoutingParams( val initialStack: List, val handleButtonButton: Boolean = false - ) : Parcelable + ) private class Component( config: Config, @@ -465,6 +461,7 @@ class ChildStackIntegrationTest { config.routingParams?.let { params -> childStack( source = navigation, + serializer = Config.serializer(), initialStack = { params.initialStack }, handleBackButton = params.handleButtonButton, childFactory = ::Component, diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt deleted file mode 100644 index 3eacc1aea..000000000 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.arkivanov.decompose.statekeeper - -import com.arkivanov.essenty.parcelable.Parcelable - -expect class ParcelableStub() : Parcelable diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestParcelableContainer.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestParcelableContainer.kt deleted file mode 100644 index 99dd61fe8..000000000 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestParcelableContainer.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.arkivanov.decompose.statekeeper - -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.ParcelableContainer -import kotlin.reflect.KClass - -class TestParcelableContainer( - var value: Parcelable? = null -) : ParcelableContainer, Parcelable by ParcelableStub() { - - @Suppress("UNCHECKED_CAST") - override fun consume(clazz: KClass): T? = - (value as T?).also { - value = null - } - - override fun set(value: Parcelable?) { - this.value = value - } -} diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestStateKeeperDispatcher.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestStateKeeperDispatcher.kt index b0402a424..63cf1e5a9 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestStateKeeperDispatcher.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestStateKeeperDispatcher.kt @@ -1,55 +1,34 @@ package com.arkivanov.decompose.statekeeper -import com.arkivanov.essenty.parcelable.Parcelable -import com.arkivanov.essenty.parcelable.ParcelableContainer -import com.arkivanov.essenty.parcelable.consume +import com.arkivanov.essenty.statekeeper.SerializableContainer import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher -import kotlin.reflect.KClass -import kotlin.test.assertFalse +import kotlinx.serialization.SerializationStrategy import kotlin.test.assertTrue class TestStateKeeperDispatcher( - val initialSavedState: ParcelableContainer? = null -) : StateKeeperDispatcher { + private val initialSavedState: SerializableContainer? = null, + private val delegate: StateKeeperDispatcher = StateKeeperDispatcher(initialSavedState), +) : StateKeeperDispatcher by delegate { - private val savedState: MutableMap? = initialSavedState?.consume()?.map - private val suppliers = HashMap Parcelable?>() + var lastSavedState: SerializableContainer? = null + private val registeredKeys = HashSet() - var lastSavedState: ParcelableContainer? = null + override fun save(): SerializableContainer = + delegate.save().also { + lastSavedState = it + } - override fun save(): ParcelableContainer { - val state = TestParcelableContainer(SavedState(suppliers.mapValuesTo(HashMap()) { TestParcelableContainer(it.value()) })) - lastSavedState = state - - return state - } - - override fun consume(key: String, clazz: KClass): T? = - savedState - ?.remove(key) - ?.consume(clazz) - - override fun register(key: String, supplier: () -> T?) { - check(key !in suppliers) - suppliers[key] = supplier + override fun register(key: String, strategy: SerializationStrategy, supplier: () -> T?) { + registeredKeys += key + delegate.register(key = key, strategy = strategy, supplier = supplier) } override fun unregister(key: String) { - check(key in suppliers) - suppliers -= key + registeredKeys -= key + delegate.unregister(key = key) } - override fun isRegistered(key: String): Boolean = key in suppliers - fun assertSupplierRegistered(key: String) { - assertTrue(key in suppliers) - } - - fun assertSupplierNotRegistered(key: String) { - assertFalse(key in suppliers) + assertTrue(key in registeredKeys) } - - private class SavedState( - val map: MutableMap - ) : Parcelable by ParcelableStub() } diff --git a/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt b/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt index b49fc31cf..4117a8749 100644 --- a/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt +++ b/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt @@ -5,7 +5,6 @@ import com.arkivanov.decompose.router.stack.navigate import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.router.stack.push import com.arkivanov.decompose.router.stack.replaceCurrent -import com.arkivanov.essenty.parcelable.Parcelable import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -437,5 +436,5 @@ class DefaultWebHistoryControllerTest { private fun isNode(): Boolean = jsTypeOf(kotlinx.browser.window) == "undefined" - private data class Config(val value: Int) : Parcelable + private data class Config(val value: Int) } diff --git a/decompose/src/nonAndroidTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt b/decompose/src/nonAndroidTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt deleted file mode 100644 index 6e8436f9e..000000000 --- a/decompose/src/nonAndroidTest/kotlin/com/arkivanov/decompose/statekeeper/ParcelableStub.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.arkivanov.decompose.statekeeper - -import com.arkivanov.essenty.parcelable.Parcelable - -actual class ParcelableStub actual constructor() : Parcelable diff --git a/deps.versions.toml b/deps.versions.toml index 398bf8ccd..ce600e9a3 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -2,8 +2,7 @@ decompose = "2.2.0-compose-experimental" kotlin = "1.9.20" -essenty = "1.3.0" -parcelizeDarwin = "0.2.3" +essenty = "2.0.0-dev01" reaktive = "1.2.3" junit = "4.13.2" jetbrainsCompose = "1.5.10" @@ -33,9 +32,6 @@ essenty-stateKeeper = { group = "com.arkivanov.essenty", name = "state-keeper", essenty-instanceKeeper = { group = "com.arkivanov.essenty", name = "instance-keeper", version.ref = "essenty" } essenty-backHandler = { group = "com.arkivanov.essenty", name = "back-handler", version.ref = "essenty" } -parcelizeDarwin-gradlePlug = { group = "com.arkivanov.parcelize.darwin", name = "gradle-plugin", version.ref = "parcelizeDarwin" } -parcelizeDarwin-runtime = { group = "com.arkivanov.parcelize.darwin", name = "runtime", version.ref = "parcelizeDarwin" } - reaktive-reaktive = { group = "com.badoo.reaktive", name = "reaktive", version.ref = "reaktive" } reaktive-reaktiveTesting = { group = "com.badoo.reaktive", name = "reaktive-testing", version.ref = "reaktive" } reaktive-coroutinesInterop = { group = "com.badoo.reaktive", name = "coroutines-interop", version.ref = "reaktive" } diff --git a/sample/app-android/src/main/java/com/arkivanov/sample/app/MainActivity.kt b/sample/app-android/src/main/java/com/arkivanov/sample/app/MainActivity.kt index 72f10ae87..fb0ceb144 100644 --- a/sample/app-android/src/main/java/com/arkivanov/sample/app/MainActivity.kt +++ b/sample/app-android/src/main/java/com/arkivanov/sample/app/MainActivity.kt @@ -19,6 +19,7 @@ class MainActivity : AppCompatActivity() { private val mode = Mode.COMPOSE + @OptIn(ExperimentalDecomposeApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/sample/app-desktop/saved_state.dat b/sample/app-desktop/saved_state.dat new file mode 100644 index 000000000..4f6acd3f1 --- /dev/null +++ b/sample/app-desktop/saved_state.dat @@ -0,0 +1 @@ +true[80,75,3,4,20,0,8,8,8,0,-106,-106,-124,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-115,89,-53,-82,54,71,13,124,-105,127,-35,-97,-44,-9,118,103,11,111,-64,18,-79,-120,32,8,68,-112,16,74,86,81,-34,-99,-70,-72,15,-53,68,71,-25,54,-33,76,-73,-37,46,-105,-53,-98,95,-66,-3,-5,-5,-1,124,-5,-18,-105,111,127,-4,-31,-17,-33,-1,-4,-29,79,127,-8,-57,63,127,-4,-37,-97,126,-6,-2,-81,-1,-6,-10,-35,79,-1,-3,-7,-121,63,71,45,103,-107,81,102,-23,-75,-44,18,-6,-86,-27,-45,-22,126,63,-6,44,113,74,-3,-115,-81,79,-116,18,81,62,107,-31,-111,-122,-27,110,-39,-73,52,44,48,-71,-48,42,-21,112,25,108,-10,-63,-9,60,-68,-66,-53,105,120,98,-108,83,90,43,-109,55,-10,-29,29,23,30,-68,-37,23,-16,-39,103,96,-121,49,-72,-12,45,3,-105,-86,-18,-21,-72,120,-68,-19,46,-99,-73,55,-36,-45,-79,-57,-26,-25,-72,-50,91,98,-106,-37,-53,-104,101,-32,-125,-117,27,120,-29,-79,85,90,116,47,91,-119,125,96,-10,-89,-73,-4,108,-30,-113,104,52,118,-13,46,46,-121,79,-106,119,-64,-121,-25,-106,-123,-5,3,-65,112,-8,-50,-61,109,60,50,105,103,-83,-123,110,-8,-116,-48,126,116,75,-121,-101,91,105,92,126,112,-69,66,-1,98,-115,-122,15,62,-73,-55,6,108,-60,43,-43,-73,99,31,108,-64,53,-49,-47,-123,-53,-19,-114,118,-91,39,91,-59,-90,-45,15,-29,-79,-49,-64,-9,92,122,28,-117,-64,85,-76,-10,-30,-47,-47,-54,112,-116,94,100,113,113,-51,114,112,97,-34,114,-62,-114,-63,58,56,12,-17,-54,-115,-20,-48,-125,39,14,30,-9,-22,-97,85,-27,-14,-127,-85,3,14,-103,53,55,-121,-79,-80,101,110,45,-128,-115,27,62,-68,-95,-29,33,12,75,56,64,100,-101,87,111,-107,123,119,-102,48,4,-115,79,116,-57,101,58,66,-67,32,-128,116,33,-10,-36,90,-119,-47,92,60,-104,-41,-1,-20,94,120,21,6,0,92,-118,79,-85,77,-121,-107,81,51,20,37,29,72,62,-90,-21,25,73,-128,30,62,-43,50,52,-107,127,-45,-45,8,-58,-79,31,112,-125,-10,96,24,-80,3,-80,53,28,2,64,9,31,-36,41,0,-45,102,120,-11,46,62,71,48,2,-73,48,107,-31,24,43,116,-94,-87,115,-60,-125,-85,66,97,7,-45,36,58,-128,14,108,-119,-11,97,-100,-97,-82,120,96,-91,-67,-71,21,79,58,51,17,63,27,-1,-41,-103,-1,32,114,-80,-50,-120,111,-83,26,-80,120,108,17,-119,10,-5,48,26,67,27,32,96,-93,25,52,-97,101,32,12,-70,-95,-55,125,-40,66,-117,6,113,89,51,109,-19,55,29,101,38,-12,101,-125,50,-128,-15,-51,-8,-48,29,-37,-97,-14,-95,-74,124,-18,79,-9,-57,31,-109,1,83,-71,-105,121,-19,54,31,30,127,-31,-63,-95,-36,37,93,-64,100,-60,-114,48,-120,101,32,-47,-85,34,7,-26,114,-21,53,79,64,43,114,1,-63,89,-103,-34,-103,-29,-12,6,76,10,7,-4,-61,-61,29,-93,90,-103,-115,21,-112,65,75,-112,102,-54,55,110,-74,70,-82,56,125,-44,109,22,-29,103,67,126,69,106,-47,-24,-31,91,101,6,-96,36,80,-13,-96,-118,99,-85,-119,-64,67,15,-9,36,-128,33,47,-119,19,-101,124,-72,76,15,8,-112,-48,55,-124,-71,-105,-126,90,-7,122,85,56,-128,-103,87,-80,-72,55,-60,39,111,11,-90,-4,-18,114,53,-64,118,-101,-61,65,63,-110,46,86,-92,93,36,-9,120,116,68,24,43,41,-105,-17,32,68,0,48,-78,73,-125,-21,91,37,-111,-106,-39,5,2,110,72,110,-127,31,91,25,25,125,58,116,102,-28,-108,29,60,86,-94,26,92,-114,-53,-111,11,116,114,29,45,36,-4,-103,76,-128,26,-16,-52,104,-19,109,-92,2,18,-60,53,-79,-45,-108,67,-5,-40,-68,109,4,50,-114,-125,-26,59,15,113,2,96,-91,-109,31,69,75,92,-12,10,81,68,6,-47,19,-118,-113,48,54,-103,57,-76,10,-98,1,49,32,102,67,127,-100,-60,16,108,78,62,7,-7,-100,-28,-72,125,-45,-71,-120,122,36,-58,55,-77,-102,65,-60,90,-67,41,-51,87,-107,-93,-5,-52,20,-48,15,-4,71,-4,-84,-109,105,-45,21,-120,-83,-59,-123,50,-8,1,75,16,22,-16,18,106,-107,2,122,76,-84,96,-22,-57,112,-84,-63,-68,54,-24,-124,78,127,-119,-24,-79,-34,-42,1,104,10,29,-114,-70,72,-74,94,-114,57,99,2,110,-30,-18,12,55,-7,107,17,-78,-82,-119,-21,101,66,19,24,-128,56,-62,104,-114,87,-86,-82,106,-117,17,-37,31,-124,-99,15,-28,-28,32,56,85,-41,-85,-127,2,67,1,-4,-21,101,98,-66,-78,-53,106,-71,-67,12,-77,-122,-48,32,15,86,-63,-128,76,-117,68,-65,118,-75,-16,-54,-27,-56,-82,-124,15,-10,-64,-127,-123,-4,110,32,-120,50,-103,-102,55,-20,94,74,3,-63,-65,63,50,-44,15,46,-123,-57,-79,107,84,63,-47,-78,-64,93,34,99,11,-121,58,-110,-62,-53,-78,-126,108,-85,46,64,12,-114,-128,94,-67,54,126,3,-67,65,50,-127,107,-71,0,-63,44,38,51,-5,3,-80,-41,-27,-114,37,-125,-18,81,104,-90,104,20,-111,-64,-19,-103,107,-14,-44,86,-11,-25,9,-82,28,-23,64,-122,-24,12,-68,-56,-83,87,119,-83,-125,-123,-16,22,117,-49,-82,37,115,-85,25,121,67,-105,89,115,-37,-76,4,-46,-7,72,-32,-47,116,-1,56,-103,-84,-125,81,-73,-68,96,-103,-48,-39,36,-77,-32,-8,-19,108,25,75,-101,109,123,-60,-123,113,-101,12,-32,-63,44,-2,-44,78,109,91,-81,-8,112,-63,26,35,56,-123,124,-59,-46,125,-100,76,-4,88,-58,30,-7,-32,35,-20,78,71,-104,-104,103,20,97,-95,116,-52,-54,-88,-14,7,124,-63,-76,91,38,76,66,20,30,102,101,12,47,-121,103,-25,81,101,107,89,111,64,70,48,78,44,7,-98,25,-14,19,85,29,87,-22,46,53,-8,-109,-59,-97,-15,34,102,-31,53,50,-120,-31,-3,60,18,-90,98,29,-78,37,-103,-98,109,-97,-119,108,-23,87,-27,48,-111,77,92,81,39,23,62,32,92,34,-125,-56,92,97,59,20,25,-42,-41,-93,68,-31,50,-95,77,-120,29,25,-7,-40,-104,-52,-73,12,113,-127,-9,100,25,-106,112,3,97,-123,28,-62,-112,40,7,12,112,-20,50,-83,-58,-122,-123,2,78,41,-127,-96,101,107,50,51,-18,66,78,98,-109,-99,-82,20,106,-73,28,76,6,112,109,114,-51,-108,-34,-20,41,7,17,-121,-69,-77,114,-124,-24,-125,57,-65,-21,-85,53,35,-59,107,56,-114,-40,-57,-12,60,76,-17,-29,125,-42,-98,98,20,-127,-122,75,-41,-74,59,-102,-42,52,-71,0,-62,60,-114,-128,110,-114,-93,45,112,-43,86,-67,-65,-118,18,-93,-126,-65,-114,-108,0,17,-90,-37,26,121,67,-121,32,-86,36,124,-81,-50,107,-118,-84,-114,72,-62,75,42,106,-24,115,62,77,-5,102,106,64,85,-11,-31,-53,-14,-66,36,40,-110,-122,-54,-106,59,112,117,85,12,-98,104,-41,68,116,-93,-90,-69,41,-118,17,-62,78,-91,33,71,-54,-68,-108,38,44,-105,31,-47,64,112,-61,19,18,122,74,-111,-6,122,-117,106,2,-28,83,-39,43,-80,36,-103,-72,-7,40,-13,-20,-12,-30,-109,84,91,-126,4,94,-14,28,15,124,13,125,-34,-5,118,-81,-86,16,-103,29,-90,74,-27,102,72,-54,93,-47,-27,16,99,41,-39,24,-41,43,-76,99,79,-15,5,29,3,112,75,-73,91,71,15,57,91,-53,79,-41,-3,105,-115,55,77,-13,-84,112,-89,37,45,16,4,88,28,17,48,-7,89,87,48,72,-16,106,23,49,-64,28,-102,-16,101,-118,-37,49,-36,-26,-126,76,47,-17,-102,80,26,41,46,-128,10,28,-104,-44,-99,-12,-27,122,-74,-52,37,42,-69,-21,109,44,-40,-120,61,9,27,-96,121,88,91,-15,-23,97,-15,116,-36,-80,40,27,54,-37,24,9,-65,-82,82,76,77,96,120,81,-47,-6,-124,102,-25,-44,-65,-93,63,6,-18,-114,-102,-60,-25,84,-34,54,55,34,83,25,119,-83,-18,-17,-53,57,-44,-27,-43,68,43,-95,98,15,88,-78,108,-126,-36,-124,32,-30,-100,-50,-38,110,-120,-23,85,75,101,85,72,119,-123,44,90,-57,-59,15,123,-88,99,61,69,125,-56,50,-34,-28,106,107,123,67,123,-51,108,98,-26,99,32,-89,-67,-43,115,-11,5,42,15,-73,50,34,106,37,-79,-37,105,-83,117,21,118,-58,84,-19,-125,-55,94,-115,-82,34,64,-24,84,30,-104,-51,3,-78,-122,-35,-85,-101,37,53,-122,-9,5,-21,102,-43,-58,-95,1,53,22,21,24,-60,-102,123,-110,76,-67,22,5,46,-103,-19,72,65,100,-95,-93,118,14,-3,-33,-35,60,-69,-14,-85,-121,-127,-97,-121,58,111,93,15,37,-64,-52,-67,-10,116,74,62,-109,59,65,-72,-102,-97,-59,-7,-30,-1,-84,34,40,-20,-108,-23,61,41,25,34,-71,103,43,4,-4,44,117,98,22,-97,48,49,-85,-46,44,41,80,40,9,-69,-100,65,50,-60,-22,55,-111,-92,-50,-80,38,-51,76,107,98,105,-38,-23,-6,-126,109,97,-2,117,17,-63,115,64,-54,-52,22,114,-89,-60,-34,-39,-98,-116,-84,-3,106,-12,16,14,51,-84,-39,123,-71,-41,-70,-26,14,6,53,81,83,-81,-69,58,54,27,-66,79,-98,19,-107,-47,-51,-110,-40,95,61,-107,-2,-64,-6,32,-4,-95,-22,-76,119,10,4,-57,-93,-47,-78,110,24,-77,111,-23,-105,-90,-117,-33,-86,15,12,92,69,-50,97,-114,-23,-77,-105,-52,-37,101,-95,-58,70,32,113,-51,-91,103,117,-126,73,56,-45,-10,-110,73,44,76,-57,76,18,36,20,-114,-3,-104,45,-39,81,10,93,-127,14,-89,123,77,-112,2,-14,100,10,-16,48,21,41,10,-22,16,-22,1,74,-75,-79,-82,-23,-36,80,61,-111,122,-64,-101,125,96,77,69,-112,83,21,-107,124,14,18,66,114,-117,76,71,54,96,-110,119,-49,12,68,-55,-57,-35,97,-51,1,68,-88,-93,-87,98,-123,-69,-97,38,27,-2,63,-104,49,-68,21,-98,-106,-80,-102,-10,-51,-87,111,-53,-59,-16,72,45,-121,89,104,88,-38,11,-37,-100,-128,-31,20,84,102,87,21,62,86,-10,68,-37,1,-28,-75,-111,69,113,28,-109,21,25,-120,-75,36,43,-78,-124,15,-89,41,-126,103,-45,-34,-12,0,-113,-75,-66,-38,82,85,-73,28,126,-120,7,87,-3,42,-28,-115,56,-47,-56,-92,101,-93,-80,61,-33,59,-39,78,89,-68,104,14,-61,124,104,22,60,-46,-39,18,-113,56,113,79,30,74,125,116,93,79,87,-102,-76,94,-44,43,-111,-88,121,-49,81,67,-84,-95,1,111,99,3,-50,-106,-13,9,0,38,-117,10,-123,86,39,-96,122,106,44,26,-51,97,-94,-101,42,90,-64,103,65,106,18,-66,-94,113,21,50,-2,118,-85,115,-44,115,-20,108,-87,53,60,-86,-22,-72,-70,-55,104,-67,-119,90,-77,-58,37,-17,106,70,115,77,104,47,103,-85,33,-73,95,-49,-125,-17,123,83,-112,37,1,57,-13,-99,68,95,45,14,-83,-23,57,44,96,-10,-112,110,-86,-77,46,-15,-89,64,94,23,-104,41,-31,-122,51,45,-79,-21,122,44,110,89,102,-27,-58,26,28,-98,-71,-100,-20,96,84,-47,-91,-110,-106,-121,16,45,-93,63,-51,26,-12,-121,-25,115,-102,21,-43,60,-49,21,54,1,101,38,-55,-40,57,-117,-112,-30,-15,-100,-90,-119,112,-82,-121,-62,-40,104,63,22,49,-99,52,18,119,119,41,-56,-114,-115,61,-58,-80,85,18,107,83,-108,118,-76,-79,-49,-86,-59,-122,-10,20,104,-113,-113,56,-115,-79,-72,-87,68,-86,90,113,-115,125,114,-108,-90,17,-100,27,48,-78,100,-51,105,-89,-121,-45,67,-51,35,5,31,23,91,-26,-80,-21,-39,-43,-14,-16,-58,-77,69,-75,36,46,84,-35,-29,69,81,-17,-7,26,88,-91,-14,-50,-39,-93,104,49,103,-71,87,-54,-116,57,76,6,-42,33,-72,-123,-54,-10,-11,4,18,49,-69,-116,90,54,-35,-62,-65,74,93,-51,94,-3,122,-90,-8,34,39,127,-123,-4,-87,-76,53,-37,-14,-73,6,32,75,-93,83,-16,-43,-18,-39,-55,84,-39,-69,-77,-123,-103,18,101,93,-128,-96,-119,-46,72,28,-3,51,-2,108,-61,-22,87,-111,30,55,-121,4,64,-38,-101,69,-69,120,13,77,-108,-56,-76,112,-44,116,95,-56,-114,40,68,26,-29,-119,-15,-109,53,-71,-71,-30,-45,106,42,125,-113,93,67,116,-90,-74,109,-71,62,-118,-43,-28,-102,-86,-23,54,-33,28,-72,5,-124,27,116,70,-122,95,-45,102,36,103,-54,-27,21,95,-122,124,-94,124,53,3,67,64,19,118,-98,4,-90,119,-81,103,88,-102,88,-23,61,-63,16,-111,83,-33,-121,-25,-108,-45,102,83,47,2,110,-6,-107,-91,126,57,85,7,19,102,73,-48,124,-106,-57,-65,-61,-53,117,-73,52,74,120,-62,-98,-83,111,79,55,14,-58,-32,48,-44,98,-41,112,-48,60,-103,72,-38,-116,-100,-4,-92,5,-35,122,123,-25,-102,126,-117,-95,-74,-44,51,-49,-18,-119,101,-13,-24,-102,-23,72,53,-47,50,46,-102,-52,104,-104,-17,-84,-110,96,86,-117,37,41,90,-105,-21,-85,76,13,19,110,120,60,-43,-52,-3,77,58,67,-6,-27,-75,17,-11,62,-70,-50,50,94,-77,7,-32,-24,-60,2,-46,61,94,-105,2,93,14,-54,-35,-119,-100,-6,84,-35,-107,68,20,11,101,-104,34,-25,-91,-37,-117,90,-15,40,-101,119,-122,56,-92,-64,-56,-16,-84,104,33,124,-120,81,-44,-106,-69,-88,-45,-114,-31,-9,31,106,-57,-34,-69,30,57,-124,100,-63,-66,39,-4,-86,-26,-68,-105,34,-98,75,75,28,-67,-128,-24,-120,71,-93,-126,-31,-66,-38,3,-62,36,-58,-95,33,-35,-55,-120,107,18,103,10,-49,-26,-118,-48,-52,81,-64,113,-75,-99,-42,17,43,44,15,-45,-67,-92,28,-50,-87,-106,-34,-63,105,-118,27,62,126,-10,-7,20,41,-51,-17,-123,60,92,-86,-87,-89,117,-16,-101,-19,-19,116,-90,-69,-50,-102,116,-74,111,75,78,-108,-11,43,-93,62,114,82,120,111,114,-91,102,-4,-98,72,-15,-3,-127,122,66,119,126,-95,41,-42,-108,-84,84,1,-40,-9,21,2,-127,-38,-118,89,47,-91,-44,-77,-70,114,26,16,126,89,-13,37,-52,-36,-98,100,73,-21,-98,-7,-67,17,37,78,123,93,-72,5,-27,-18,-52,56,-114,7,81,-90,78,-126,47,13,85,20,-79,49,9,-121,31,-66,65,-84,6,-55,77,61,30,-71,-110,-17,109,-84,82,35,11,47,-119,57,18,113,93,-124,117,-12,-74,100,-17,44,-17,-15,38,99,73,-82,30,-99,-98,-25,-65,105,16,-88,-21,98,-116,93,-54,104,-102,-122,21,41,29,-73,7,112,-110,113,91,-60,-96,114,-105,-17,1,-89,24,127,-24,53,23,115,50,-84,25,56,43,28,30,-108,-21,95,109,123,91,-55,-63,-125,85,-97,94,-98,-87,86,-8,-123,-27,-15,124,41,-45,74,-108,-39,-28,-99,-56,87,70,52,73,-17,4,13,-92,-105,125,43,-101,-109,-124,79,-49,-69,89,84,36,109,-8,-30,65,115,-25,55,6,-65,41,-48,-74,7,6,126,-31,-85,-105,46,126,-7,-21,-69,27,39,34,-4,-9,55,94,37,-1,-98,21,127,-49,-105,119,-27,-53,-63,119,-91,-27,-9,124,-105,78,-28,-38,-8,-6,-53,-81,-65,-2,15,80,75,7,8,15,101,-112,97,-121,12,0,0,15,31,0,0,80,75,1,2,20,0,20,0,8,8,8,0,-106,-106,-124,87,15,101,-112,97,-121,12,0,0,15,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,75,5,6,0,0,0,0,1,0,1,0,46,0,0,0,-75,12,0,0,0,0] \ No newline at end of file diff --git a/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt b/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt index 2ad792c54..2a1b7bc7d 100644 --- a/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt +++ b/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt @@ -23,25 +23,26 @@ import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.extensions.compose.jetbrains.lifecycle.LifecycleController import com.arkivanov.essenty.lifecycle.LifecycleRegistry -import com.arkivanov.essenty.parcelable.ParcelableContainer import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher import com.arkivanov.sample.shared.dynamicfeatures.dynamicfeature.DefaultFeatureInstaller +import com.arkivanov.sample.shared.readSerializableContainer import com.arkivanov.sample.shared.root.DefaultRootComponent import com.arkivanov.sample.shared.root.RootContent +import com.arkivanov.sample.shared.writeToFile import com.badoo.reaktive.coroutinesinterop.asScheduler import com.badoo.reaktive.scheduler.overrideSchedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import java.io.File -import java.io.ObjectInputStream -import java.io.ObjectOutputStream + +private const val SAVED_STATE_FILE_NAME = "saved_state.dat" @OptIn(ExperimentalDecomposeApi::class, ExperimentalCoroutinesApi::class) fun main() { overrideSchedulers(main = Dispatchers.Main::asScheduler) val lifecycle = LifecycleRegistry() - val stateKeeper = StateKeeperDispatcher(tryRestoreStateFromFile()) + val stateKeeper = StateKeeperDispatcher(File(SAVED_STATE_FILE_NAME).readSerializableContainer()) val root = runOnUiThread { @@ -70,7 +71,7 @@ fun main() { if (isCloseRequested) { SaveStateDialog( - onSaveState = { saveStateToFile(stateKeeper.save()) }, + onSaveState = { stateKeeper.save().writeToFile(File(SAVED_STATE_FILE_NAME)) }, onExitApplication = ::exitApplication, onDismiss = { isCloseRequested = false }, ) @@ -116,22 +117,3 @@ private fun SaveStateDialog( modifier = Modifier.width(400.dp), ) } - -private const val SAVED_STATE_FILE_NAME = "saved_state.dat" - -private fun saveStateToFile(state: ParcelableContainer) { - ObjectOutputStream(File(SAVED_STATE_FILE_NAME).outputStream()).use { output -> - output.writeObject(state) - } -} - -private fun tryRestoreStateFromFile(): ParcelableContainer? = - File(SAVED_STATE_FILE_NAME).takeIf(File::exists)?.let { file -> - try { - ObjectInputStream(file.inputStream()).use(ObjectInputStream::readObject) as ParcelableContainer - } catch (e: Exception) { - null - } finally { - file.delete() - } - } diff --git a/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate index 9d0313ba6682a7720b0bfd4bb1bc13452c5fe71f..b320fa017b542ca48e1dc8f16d4b29d9bb95d607 100644 GIT binary patch delta 13898 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 OKf*_jzwPh+cm5wci)m5- delta 13698 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 Bool { - CodingKt.encodeParcelable(coder, value: stateKeeper.save(), key: "savedState") + StateKeeperUtilsKt.save(coder: coder, state: stateKeeper.save()) return true } func application(_ application: UIApplication, shouldRestoreSecureApplicationState coder: NSCoder) -> Bool { - do { - let savedState = try CodingKt.decodeParcelable(coder, key: "savedState") as! ParcelableParcelableContainer - stateKeeper = StateKeeperDispatcherKt.StateKeeperDispatcher(savedState: savedState) - return true - } catch { - return false - } + stateKeeper = StateKeeperDispatcherKt.StateKeeperDispatcher(savedState: StateKeeperUtilsKt.restore(coder: coder)) + return true } } diff --git a/sample/app-ios/app-ios/app_iosApp.swift b/sample/app-ios/app-ios/app_iosApp.swift index bd20440a3..ca882e164 100644 --- a/sample/app-ios/app-ios/app_iosApp.swift +++ b/sample/app-ios/app-ios/app_iosApp.swift @@ -12,7 +12,7 @@ import Shared struct app_iosApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate: AppDelegate - + var body: some Scene { WindowGroup { RootView(appDelegate.root) @@ -22,7 +22,7 @@ struct app_iosApp: App { class AppDelegate: NSObject, UIApplicationDelegate { private var stateKeeper = StateKeeperDispatcherKt.StateKeeperDispatcher(savedState: nil) - + lazy var root: RootComponent = DefaultRootComponent( componentContext: DefaultComponentContext( lifecycle: ApplicationLifecycle(), @@ -34,19 +34,14 @@ class AppDelegate: NSObject, UIApplicationDelegate { deepLink: DefaultRootComponentDeepLinkNone.shared, webHistoryController: nil ) - + func application(_ application: UIApplication, shouldSaveSecureApplicationState coder: NSCoder) -> Bool { - CodingKt.encodeParcelable(coder, value: stateKeeper.save(), key: "savedState") + StateKeeperUtilsKt.save(coder: coder, state: stateKeeper.save()) return true } func application(_ application: UIApplication, shouldRestoreSecureApplicationState coder: NSCoder) -> Bool { - do { - let savedState = try CodingKt.decodeParcelable(coder, key: "savedState") as! ParcelableParcelableContainer - stateKeeper = StateKeeperDispatcherKt.StateKeeperDispatcher(savedState: savedState) - return true - } catch { - return false - } + stateKeeper = StateKeeperDispatcherKt.StateKeeperDispatcher(savedState: StateKeeperUtilsKt.restore(coder: coder)) + return true } } diff --git a/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt b/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt index fef1ffef4..ab30306b1 100644 --- a/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt +++ b/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.ui.Modifier import androidx.compose.ui.window.Window import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.resume import com.arkivanov.essenty.lifecycle.stop @@ -15,6 +16,7 @@ import web.dom.DocumentVisibilityState import web.dom.document import web.events.EventType +@OptIn(ExperimentalDecomposeApi::class) fun main() { val lifecycle = LifecycleRegistry() diff --git a/sample/shared/compose/build.gradle.kts b/sample/shared/compose/build.gradle.kts index 6ee286fa7..1a8a35169 100644 --- a/sample/shared/compose/build.gradle.kts +++ b/sample/shared/compose/build.gradle.kts @@ -40,9 +40,6 @@ kotlin { // Optional, only if you need state preservation on Darwin (Apple) targets export(deps.essenty.stateKeeper) - - // Optional, only if you need state preservation on Darwin (Apple) targets - export(deps.parcelizeDarwin.runtime) } } } diff --git a/sample/shared/shared/build.gradle.kts b/sample/shared/shared/build.gradle.kts index 0a2bfedd0..e07289a83 100644 --- a/sample/shared/shared/build.gradle.kts +++ b/sample/shared/shared/build.gradle.kts @@ -9,7 +9,6 @@ import org.jetbrains.kotlin.konan.target.Family plugins { id("kotlin-multiplatform") id("com.android.library") - id("kotlin-parcelize") id("kotlinx-serialization") id("com.arkivanov.gradle.setup") } @@ -37,9 +36,6 @@ kotlin { // Optional, only if you need state preservation on Darwin (Apple) targets export(deps.essenty.stateKeeper) - - // Optional, only if you need state preservation on Darwin (Apple) targets - export(deps.parcelizeDarwin.runtime) } } } @@ -47,10 +43,13 @@ kotlin { setupSourceSets { val android by bundle() val js by bundle() + val ios by bundle() val nonAndroid by bundle() - nonAndroid.dependsOn(common) - (allSet - android).dependsOn(nonAndroid) + nonAndroid dependsOn common + ios dependsOn nonAndroid + iosSet dependsOn ios + (allSet - iosSet - android).dependsOn(nonAndroid) common.main.dependencies { api(project(":decompose")) @@ -59,6 +58,7 @@ kotlin { api(deps.essenty.stateKeeper) api(deps.essenty.backHandler) implementation(deps.reaktive.reaktive) + implementation(deps.jetbrains.kotlinx.kotlinxSerializationJson) } common.test.dependencies { diff --git a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/Json.kt b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/Json.kt new file mode 100644 index 000000000..236af1c9b --- /dev/null +++ b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/Json.kt @@ -0,0 +1,8 @@ +package com.arkivanov.sample.shared + +import kotlinx.serialization.json.Json + +internal val json = + Json { + allowStructuredMapKeys = true + } diff --git a/sample/shared/shared/src/commonTest/kotlin/com/arkivanov/sample/shared/root/RootComponentIntegrationTest.kt b/sample/shared/shared/src/commonTest/kotlin/com/arkivanov/sample/shared/root/RootComponentIntegrationTest.kt index 2961c5589..662c6ee2f 100644 --- a/sample/shared/shared/src/commonTest/kotlin/com/arkivanov/sample/shared/root/RootComponentIntegrationTest.kt +++ b/sample/shared/shared/src/commonTest/kotlin/com/arkivanov/sample/shared/root/RootComponentIntegrationTest.kt @@ -1,6 +1,7 @@ package com.arkivanov.sample.shared.root import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.router.stack.active import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.resume @@ -93,6 +94,7 @@ class RootComponentIntegrationTest { assertTrue(activeChild is CustomNavigationChild) } + @OptIn(ExperimentalDecomposeApi::class) private fun createComponent( deepLink: DeepLink = DeepLink.None, ) { diff --git a/sample/shared/shared/src/iosMain/kotlin/com/arkivanov/sample/shared/StateKeeperUtils.kt b/sample/shared/shared/src/iosMain/kotlin/com/arkivanov/sample/shared/StateKeeperUtils.kt new file mode 100644 index 000000000..2e55d5b56 --- /dev/null +++ b/sample/shared/shared/src/iosMain/kotlin/com/arkivanov/sample/shared/StateKeeperUtils.kt @@ -0,0 +1,25 @@ +package com.arkivanov.sample.shared + +import com.arkivanov.essenty.statekeeper.SerializableContainer +import kotlinx.cinterop.BetaInteropApi +import kotlinx.cinterop.ExperimentalForeignApi +import platform.Foundation.NSCoder +import platform.Foundation.NSString +import platform.Foundation.decodeTopLevelObjectOfClass +import platform.Foundation.encodeObject + +@Suppress("unused") // Used in Swift +fun save(coder: NSCoder, state: SerializableContainer) { + coder.encodeObject(`object` = json.encodeToString(SerializableContainer.serializer(), state), forKey = "state") +} + +@Suppress("unused") // Used in Swift +@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class) +fun restore(coder: NSCoder): SerializableContainer? = + (coder.decodeTopLevelObjectOfClass(aClass = NSString, forKey = "state", error = null) as String?)?.let { + try { + json.decodeFromString(SerializableContainer.serializer(), it) + } catch (e: Exception) { + null + } + } diff --git a/sample/shared/shared/src/jvmMain/kotlin/com/arkivanov/sample/shared/StateKeeperUtils.kt b/sample/shared/shared/src/jvmMain/kotlin/com/arkivanov/sample/shared/StateKeeperUtils.kt new file mode 100644 index 000000000..4d9b9721b --- /dev/null +++ b/sample/shared/shared/src/jvmMain/kotlin/com/arkivanov/sample/shared/StateKeeperUtils.kt @@ -0,0 +1,24 @@ +package com.arkivanov.sample.shared + +import com.arkivanov.essenty.statekeeper.SerializableContainer +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.decodeFromStream +import kotlinx.serialization.json.encodeToStream +import java.io.File + +@OptIn(ExperimentalSerializationApi::class) +fun SerializableContainer.writeToFile(file: File) { + file.outputStream().use { output -> + json.encodeToStream(SerializableContainer.serializer(), this, output) + } +} + +@OptIn(ExperimentalSerializationApi::class) +fun File.readSerializableContainer(): SerializableContainer? = + takeIf(File::exists)?.inputStream()?.use { input -> + try { + json.decodeFromStream(SerializableContainer.serializer(), input) + } catch (e: Exception) { + null + } + } From a559bad3eb31bee4ff479d25fef84adcad80ef43 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Mon, 4 Dec 2023 21:42:12 +0000 Subject: [PATCH 29/89] Removed deprecated Value#subscribe and Value#unsubscribe methods, renamed Value#observe to Value#subscribe --- decompose/api/android/decompose.api | 8 ++-- decompose/api/jvm/decompose.api | 8 ++-- .../decompose/value/MutableValueBuilder.kt | 24 +++++------ .../com/arkivanov/decompose/value/Value.kt | 31 +------------- .../com/arkivanov/decompose/value/ValueExt.kt | 8 ++-- .../arkivanov/decompose/value/operator/Map.kt | 37 ++--------------- .../decompose/value/MutableValueTest.kt | 14 +++---- .../decompose/value/operator/ValueMapTest.kt | 38 +++++++++--------- .../com/arkivanov/decompose/router/Utils.kt | 2 +- .../AbstractMutableValueThreadingTest.kt | 8 ++-- .../operator/AbstractValueMapThreadingTest.kt | 2 +- .../android/stack/StackRouterView.kt | 4 +- .../compose/jetbrains/SubscribeAsState.kt | 2 +- .../UserInterfaceState.xcuserstate | Bin 27977 -> 27541 bytes .../DecomposeHelpers/ObservableValue.swift | 2 +- .../shared/counters/counter/CounterView.kt | 6 +-- .../arkivanov/sample/shared/root/RootView.kt | 4 +- .../com/arkivanov/sample/shared/Utils.kt | 5 +-- 18 files changed, 66 insertions(+), 137 deletions(-) diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index bbb26bb14..b03747386 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -364,15 +364,13 @@ public final class com/arkivanov/decompose/value/ObserveLifecycleMode : java/lan public abstract class com/arkivanov/decompose/value/Value { public fun ()V public abstract fun getValue ()Ljava/lang/Object; - public final fun observe (Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/Cancellation; - public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)V - public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V + public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/Cancellation; } public final class com/arkivanov/decompose/value/ValueExtKt { public static final fun getValue (Lcom/arkivanov/decompose/value/Value;Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object; - public static final fun observe (Lcom/arkivanov/decompose/value/Value;Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/decompose/value/ObserveLifecycleMode;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun observe$default (Lcom/arkivanov/decompose/value/Value;Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/decompose/value/ObserveLifecycleMode;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V + public static final fun subscribe (Lcom/arkivanov/decompose/value/Value;Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/decompose/value/ObserveLifecycleMode;Lkotlin/jvm/functions/Function1;)V + public static synthetic fun subscribe$default (Lcom/arkivanov/decompose/value/Value;Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/decompose/value/ObserveLifecycleMode;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V } public final class com/arkivanov/decompose/value/operator/MapKt { diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api index 97a65ab17..5d1632ca5 100644 --- a/decompose/api/jvm/decompose.api +++ b/decompose/api/jvm/decompose.api @@ -350,15 +350,13 @@ public final class com/arkivanov/decompose/value/ObserveLifecycleMode : java/lan public abstract class com/arkivanov/decompose/value/Value { public fun ()V public abstract fun getValue ()Ljava/lang/Object; - public final fun observe (Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/Cancellation; - public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)V - public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V + public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/Cancellation; } public final class com/arkivanov/decompose/value/ValueExtKt { public static final fun getValue (Lcom/arkivanov/decompose/value/Value;Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object; - public static final fun observe (Lcom/arkivanov/decompose/value/Value;Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/decompose/value/ObserveLifecycleMode;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun observe$default (Lcom/arkivanov/decompose/value/Value;Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/decompose/value/ObserveLifecycleMode;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V + public static final fun subscribe (Lcom/arkivanov/decompose/value/Value;Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/decompose/value/ObserveLifecycleMode;Lkotlin/jvm/functions/Function1;)V + public static synthetic fun subscribe$default (Lcom/arkivanov/decompose/value/Value;Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/decompose/value/ObserveLifecycleMode;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V } public final class com/arkivanov/decompose/value/operator/MapKt { diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/MutableValueBuilder.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/MutableValueBuilder.kt index 622db28b5..36d146394 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/MutableValueBuilder.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/MutableValueBuilder.kt @@ -1,12 +1,12 @@ package com.arkivanov.decompose.value +import com.arkivanov.decompose.Cancellation import com.arkivanov.decompose.Lock import com.arkivanov.decompose.synchronized /** * Returns a new instance of [MutableValue] initialized with the provided [initialValue]. */ -@Suppress("FunctionName") // Factory function fun MutableValue(initialValue: T): MutableValue = MutableValueImpl(initialValue) private class MutableValueImpl(initialValue: T) : MutableValue() { @@ -70,13 +70,13 @@ private class MutableValueImpl(initialValue: T) : MutableValue() { } } - @Deprecated( - "Calling this method from Swift leaks the observer, " + - "because Kotlin wraps the function passed from Swift every time the method is called. " + - "Please use the new `observe` method which returns `Disposable`.", - level = DeprecationLevel.WARNING, - ) - override fun subscribe(observer: (T) -> Unit) { + override fun subscribe(observer: (T) -> Unit): Cancellation { + subscribeObserver(observer) + + return Cancellation { unsubscribeObserver(observer) } + } + + private fun subscribeObserver(observer: (T) -> Unit) { lock.synchronized { if (observer in observers) { return @@ -103,13 +103,7 @@ private class MutableValueImpl(initialValue: T) : MutableValue() { } } - @Deprecated( - "Calling this method from Swift doesn't have any effect, " + - "because Kotlin wraps the function passed from Swift every time the method is called. " + - "Please use the new `observe` method which returns `Disposable`.", - level = DeprecationLevel.WARNING, - ) - override fun unsubscribe(observer: (T) -> Unit) { + private fun unsubscribeObserver(observer: (T) -> Unit) { lock.synchronized { observers -= observer } } } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/Value.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/Value.kt index 4bc1e9331..a4f665a60 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/Value.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/Value.kt @@ -14,39 +14,10 @@ abstract class Value { */ abstract val value: T - /** - * Subscribes the provided [observer] for value updates. The current value is emitted synchronously on subscription. - */ - @Deprecated( - message = "Calling this method from Swift leaks the observer, " + - "because Kotlin wraps the function passed from Swift every time the method is called. " + - "Please use the new `observe` method which returns `Disposable`.", - level = DeprecationLevel.WARNING, - ) - abstract fun subscribe(observer: (T) -> Unit) - - /** - * Unsubscribes the provided [observer] from value updates. - */ - @Deprecated( - message = "Calling this method from Swift doesn't have any effect, " + - "because Kotlin wraps the function passed from Swift every time the method is called. " + - "Please use the new `observe` method which returns `Disposable`.", - level = DeprecationLevel.WARNING, - ) - abstract fun unsubscribe(observer: (T) -> Unit) - /** * Subscribes the provided [observer] for value updates. The current value is emitted synchronously on subscription. * - * Note: most likely this method will be renamed to `subscribe` in the next major release, once deprecated methods are removed. - * * @return [Cancellation] token to cancel the subscription. */ - @Suppress("DEPRECATION") - fun observe(observer: (T) -> Unit): Cancellation { - subscribe(observer) - - return Cancellation { unsubscribe(observer) } - } + abstract fun subscribe(observer: (T) -> Unit): Cancellation } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/ValueExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/ValueExt.kt index 9542f9773..df0437e65 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/ValueExt.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/ValueExt.kt @@ -7,7 +7,7 @@ import kotlin.reflect.KProperty operator fun Value.getValue(thisRef: Any?, property: KProperty<*>): T = value -fun Value.observe( +fun Value.subscribe( lifecycle: Lifecycle, mode: ObserveLifecycleMode = ObserveLifecycleMode.START_STOP, observer: (T) -> Unit, @@ -17,19 +17,19 @@ fun Value.observe( when (mode) { ObserveLifecycleMode.CREATE_DESTROY -> lifecycle.subscribe( - onCreate = { cancellation = observe(observer) }, + onCreate = { cancellation = subscribe(observer) }, onDestroy = { cancellation?.cancel() }, ) ObserveLifecycleMode.START_STOP -> lifecycle.subscribe( - onStart = { cancellation = observe(observer) }, + onStart = { cancellation = subscribe(observer) }, onStop = { cancellation?.cancel() }, ) ObserveLifecycleMode.RESUME_PAUSE -> lifecycle.subscribe( - onResume = { cancellation = observe(observer) }, + onResume = { cancellation = subscribe(observer) }, onPause = { cancellation?.cancel() }, ) }.let {} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/operator/Map.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/operator/Map.kt index 40a761003..94b2d7da0 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/operator/Map.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/value/operator/Map.kt @@ -1,5 +1,6 @@ package com.arkivanov.decompose.value.operator +import com.arkivanov.decompose.Cancellation import com.arkivanov.decompose.Lock import com.arkivanov.decompose.synchronized import com.arkivanov.decompose.value.Value @@ -14,7 +15,6 @@ private class MappedValue( private val lock = Lock() private var lastUpstreamValue: T = upstream.value private var lastMappedValue: R = mapper(lastUpstreamValue) - private var observers = HashMap<(R) -> Unit, (T) -> Unit>() override val value: R get() = mapCached(upstream.value) @@ -28,37 +28,6 @@ private class MappedValue( lastMappedValue } - @Deprecated( - "Calling this method from Swift leaks the observer, " + - "because Kotlin wraps the function passed from Swift every time the method is called. " + - "Please use the new `observe` method which returns `Disposable`.", - level = DeprecationLevel.WARNING, - ) - override fun subscribe(observer: (R) -> Unit) { - val upstreamObserver: (T) -> Unit = { value -> observer(mapCached(value)) } - - lock.synchronized { - if (observer in observers) { - return - } - - observers[observer] = upstreamObserver - } - - @Suppress("DEPRECATION") - upstream.subscribe(upstreamObserver) - } - - @Deprecated( - "Calling this method from Swift doesn't have any effect, " + - "because Kotlin wraps the function passed from Swift every time the method is called. " + - "Please use the new `observe` method which returns `Disposable`.", - level = DeprecationLevel.WARNING, - ) - override fun unsubscribe(observer: (R) -> Unit) { - val upstreamObserver = lock.synchronized { observers.remove(observer) } ?: return - - @Suppress("DEPRECATION") - upstream.unsubscribe(upstreamObserver) - } + override fun subscribe(observer: (R) -> Unit): Cancellation = + upstream.subscribe { observer(mapCached(it)) } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/MutableValueTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/MutableValueTest.kt index da8dfc1a5..41d54c11e 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/MutableValueTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/MutableValueTest.kt @@ -27,7 +27,7 @@ class MutableValueTest { fun WHEN_subscribe_THEN_current_value_emitted() { val values = ArrayList() - value.observe { values += it } + value.subscribe { values += it } assertContentEquals(listOf(0), values) } @@ -37,7 +37,7 @@ class MutableValueTest { val values = List(10) { ArrayList() } repeat(10) { index -> - value.observe { values[index] += it } + value.subscribe { values[index] += it } } value.value = 1 @@ -50,7 +50,7 @@ class MutableValueTest { @Test fun GIVEN_unsubscribed_WHEN_value_changed_THEN_not_emitted() { val values = ArrayList() - val cancellation = value.observe { values += it } + val cancellation = value.subscribe { values += it } cancellation.cancel() values.clear() @@ -62,8 +62,8 @@ class MutableValueTest { @Test fun GIVEN_multiple_subscribes_and_one_unsubscribed_WHEN_value_changed_THEN_value_emitted_to_subscribed() { val values = ArrayList() - val cancellation = value.observe {} - value.observe { values += it } + val cancellation = value.subscribe {} + value.subscribe { values += it } cancellation.cancel() values.clear() @@ -75,8 +75,8 @@ class MutableValueTest { @Test fun GIVEN_multiple_subscribes_and_one_unsubscribed_WHEN_value_changed_THEN_value_not_emitted_to_unsubscribed() { val values = ArrayList() - val cancellation = value.observe { values += it } - value.observe {} + val cancellation = value.subscribe { values += it } + value.subscribe {} cancellation.cancel() values.clear() diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/operator/ValueMapTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/operator/ValueMapTest.kt index fbda96e89..d54834ac5 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/operator/ValueMapTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/operator/ValueMapTest.kt @@ -25,7 +25,7 @@ class ValueMapTest { @Test fun GIVEN_subscribed_WHEN_upstream_changed_THEN_value_mapped() { - mapped.observe {} + mapped.subscribe {} upstream.value = "abcd" val value = mapped.value @@ -37,7 +37,7 @@ class ValueMapTest { fun WHEN_subscribe_THEN_current_value_emitted() { val values = ArrayList() - mapped.observe { values += it } + mapped.subscribe { values += it } assertContentEquals(listOf(3), values) } @@ -47,7 +47,7 @@ class ValueMapTest { val values = List(10) { ArrayList() } repeat(10) { index -> - mapped.observe { values[index] += it } + mapped.subscribe { values[index] += it } } upstream.value = "abcd" @@ -60,7 +60,7 @@ class ValueMapTest { @Test fun GIVEN_unsubscribed_WHEN_value_changed_THEN_not_emitted() { val values = ArrayList() - val cancellation = mapped.observe { values += it } + val cancellation = mapped.subscribe { values += it } cancellation.cancel() values.clear() @@ -72,8 +72,8 @@ class ValueMapTest { @Test fun GIVEN_multiple_subscribes_and_one_unsubscribed_WHEN_value_changed_THEN_value_emitted_to_subscribed() { val values = ArrayList() - val cancellation = mapped.observe {} - mapped.observe { values += it } + val cancellation = mapped.subscribe {} + mapped.subscribe { values += it } cancellation.cancel() values.clear() @@ -85,8 +85,8 @@ class ValueMapTest { @Test fun GIVEN_multiple_subscribes_and_one_unsubscribed_WHEN_value_changed_THEN_value_not_emitted_to_unsubscribed() { val values = ArrayList() - val cancellation = mapped.observe { values += it } - mapped.observe {} + val cancellation = mapped.subscribe { values += it } + mapped.subscribe {} cancellation.cancel() values.clear() @@ -106,9 +106,9 @@ class ValueMapTest { it.length } - mapped.observe {} - mapped.observe {} - mapped.observe {} + mapped.subscribe {} + mapped.subscribe {} + mapped.subscribe {} assertEquals(1, count) } @@ -124,9 +124,9 @@ class ValueMapTest { it.length } - mapped.observe {} - mapped.observe {} - mapped.observe {} + mapped.subscribe {} + mapped.subscribe {} + mapped.subscribe {} count = 0 upstream.value = "abcd" @@ -145,11 +145,11 @@ class ValueMapTest { it.length } - mapped.observe {} - mapped.observe {} + mapped.subscribe {} + mapped.subscribe {} upstream.value = "abcd" count = 0 - mapped.observe {} + mapped.subscribe {} assertEquals(0, count) @@ -166,8 +166,8 @@ class ValueMapTest { it.length } - mapped.observe {} - mapped.observe {} + mapped.subscribe {} + mapped.subscribe {} upstream.value = "abcd" count = 0 requireNotNull(upstream.value) diff --git a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/Utils.kt b/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/Utils.kt index d6ef3a180..b1fc31f31 100644 --- a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/Utils.kt +++ b/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/Utils.kt @@ -34,7 +34,7 @@ internal fun List.findFirstDifferentIndex(other: List): Int { internal fun Value.subscribe(observer: (new: T, old: T) -> Unit) { var old = value - observe { new -> + subscribe { new -> val tmp = old old = new observer(new, tmp) diff --git a/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/AbstractMutableValueThreadingTest.kt b/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/AbstractMutableValueThreadingTest.kt index 4cad19ea9..31535a5a0 100644 --- a/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/AbstractMutableValueThreadingTest.kt +++ b/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/AbstractMutableValueThreadingTest.kt @@ -16,7 +16,7 @@ abstract class AbstractMutableValueThreadingTest : AbstractThreadingTest() { var lastValue = 0 var counter = 0 - value.observe { + value.subscribe { repeat(1000) { counter++ } lastValue = it repeat(1000) { counter-- } @@ -41,7 +41,7 @@ abstract class AbstractMutableValueThreadingTest : AbstractThreadingTest() { var lastValue = 0 var counter = 0 - value.observe { + value.subscribe { repeat(1000) { counter++ } lastValue = it repeat(1000) { counter-- } @@ -68,7 +68,7 @@ abstract class AbstractMutableValueThreadingTest : AbstractThreadingTest() { var counter = 0 repeat(3) { - value.observe {} + value.subscribe {} } race { threadIndex -> @@ -76,7 +76,7 @@ abstract class AbstractMutableValueThreadingTest : AbstractThreadingTest() { value.update { it + 1 } if ((threadIndex == 0) && (index == iterationCount / 2)) { - value.observe { + value.subscribe { repeat(1000) { counter++ } lastValue = it repeat(1000) { counter-- } diff --git a/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/operator/AbstractValueMapThreadingTest.kt b/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/operator/AbstractValueMapThreadingTest.kt index d80625e38..ca287d9dd 100644 --- a/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/operator/AbstractValueMapThreadingTest.kt +++ b/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/operator/AbstractValueMapThreadingTest.kt @@ -22,7 +22,7 @@ abstract class AbstractValueMapThreadingTest : AbstractThreadingTest() { val counters = IntArray(observerCount) repeat(observerCount) { index -> - value.observe { + value.subscribe { repeat(1000) { counters[index]++ } lastValues[index] = it repeat(1000) { counters[index]-- } diff --git a/extensions-android/src/main/java/com/arkivanov/decompose/extensions/android/stack/StackRouterView.kt b/extensions-android/src/main/java/com/arkivanov/decompose/extensions/android/stack/StackRouterView.kt index 82b068c6c..151ab0896 100644 --- a/extensions-android/src/main/java/com/arkivanov/decompose/extensions/android/stack/StackRouterView.kt +++ b/extensions-android/src/main/java/com/arkivanov/decompose/extensions/android/stack/StackRouterView.kt @@ -20,7 +20,7 @@ import com.arkivanov.decompose.hashString import com.arkivanov.decompose.lifecycle.MergedLifecycle import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.value.Value -import com.arkivanov.decompose.value.observe +import com.arkivanov.decompose.value.subscribe import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.destroy @@ -43,7 +43,7 @@ class StackRouterView @JvmOverloads constructor( lifecycle: Lifecycle, replaceChildView: ViewContext.(parent: ViewGroup, newStack: ChildStack, oldStack: ChildStack?) -> Unit, ) { - stack.observe(lifecycle) { + stack.subscribe(lifecycle) { onStackChanged(it, lifecycle, replaceChildView) } } diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/SubscribeAsState.kt b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/SubscribeAsState.kt index 67246b0b1..99856df1d 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/SubscribeAsState.kt +++ b/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/SubscribeAsState.kt @@ -20,7 +20,7 @@ fun Value.subscribeAsState(policy: SnapshotMutationPolicy = stru val state = remember(this, policy) { mutableStateOf(value, policy) } DisposableEffect(this) { - val disposable = observe { state.value = it } + val disposable = subscribe { state.value = it } onDispose { disposable.cancel() } } diff --git a/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate index b320fa017b542ca48e1dc8f16d4b29d9bb95d607..95ab41aad3b2209ae50397fc46601da7abbdcc82 100644 GIT binary patch delta 14621 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 delta 14625 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 : ObservableObject { init(_ value: Value) { self.value = value.value - self.cancellation = value.observe { [weak self] value in self?.value = value } + self.cancellation = value.subscribe { [weak self] value in self?.value = value } } deinit { diff --git a/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterView.kt b/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterView.kt index 31410148f..ac27dd692 100644 --- a/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterView.kt +++ b/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterView.kt @@ -7,7 +7,7 @@ import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.extensions.android.ViewContext import com.arkivanov.decompose.extensions.android.context import com.arkivanov.decompose.extensions.android.layoutInflater -import com.arkivanov.decompose.value.observe +import com.arkivanov.decompose.value.subscribe import com.arkivanov.essenty.lifecycle.subscribe import com.arkivanov.sample.shared.R import com.arkivanov.sample.shared.dialog.DialogComponent @@ -28,7 +28,7 @@ internal fun ViewContext.CounterView(component: CounterComponent): View { nextButton.setOnClickListener { component.onNextClicked() } prevButton.setOnClickListener { component.onPrevClicked() } - component.model.observe(lifecycle) { model -> + component.model.subscribe(lifecycle) { model -> toolbar.title = model.title if (model.isBackEnabled) { @@ -42,7 +42,7 @@ internal fun ViewContext.CounterView(component: CounterComponent): View { } var dialog: AlertDialog? = null - component.dialogSlot.observe(lifecycle) { model -> + component.dialogSlot.subscribe(lifecycle) { model -> val dialogComponent: DialogComponent? = model.child?.instance if ((dialogComponent != null) && (dialog == null)) { dialog = showDialog(component = dialogComponent) diff --git a/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/root/RootView.kt b/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/root/RootView.kt index aabf1d599..fc79e1b27 100644 --- a/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/root/RootView.kt +++ b/sample/shared/shared/src/androidMain/kotlin/com/arkivanov/sample/shared/root/RootView.kt @@ -5,7 +5,7 @@ import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.extensions.android.ViewContext import com.arkivanov.decompose.extensions.android.layoutInflater import com.arkivanov.decompose.extensions.android.stack.StackRouterView -import com.arkivanov.decompose.value.observe +import com.arkivanov.decompose.value.subscribe import com.arkivanov.sample.shared.R import com.arkivanov.sample.shared.beginDelayedSlideTransition import com.arkivanov.sample.shared.counters.CountersView @@ -64,7 +64,7 @@ fun ViewContext.RootView(component: RootComponent): View { navigationView.setOnNavigationItemSelectedListener(listener) - component.childStack.observe(lifecycle) { state -> + component.childStack.subscribe(lifecycle) { state -> navigationView.setOnNavigationItemSelectedListener(null) navigationView.selectedItemId = diff --git a/sample/shared/shared/src/jsMain/kotlin/com/arkivanov/sample/shared/Utils.kt b/sample/shared/shared/src/jsMain/kotlin/com/arkivanov/sample/shared/Utils.kt index 94b286811..195102776 100644 --- a/sample/shared/shared/src/jsMain/kotlin/com/arkivanov/sample/shared/Utils.kt +++ b/sample/shared/shared/src/jsMain/kotlin/com/arkivanov/sample/shared/Utils.kt @@ -12,9 +12,8 @@ internal fun Value.useAsState(): StateInstance { val (_, set) = state useEffectOnce { - val observer: (T) -> Unit = { set(it) } - subscribe(observer) - cleanup { unsubscribe(observer) } + val cancellation = subscribe { set(it) } + cleanup { cancellation.cancel() } } return state From 2adcc25534d2c1df842c78e2bc6200f96144480c Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Tue, 5 Dec 2023 21:31:11 +0000 Subject: [PATCH 30/89] Added STARTED status to ChildNavState --- decompose/api/android/decompose.api | 5 +- decompose/api/jvm/decompose.api | 5 +- .../router/children/ChildNavState.kt | 20 +- .../router/children/ChildrenNavigator.kt | 172 ++++---- .../router/pages/ChildPagesFactory.kt | 12 +- .../decompose/router/slot/ChildSlotFactory.kt | 2 +- .../router/stack/ChildStackFactory.kt | 2 +- .../children/ChildrenBackPressedTest.kt | 209 ++++++++-- .../router/children/ChildrenBasicTest.kt | 36 +- .../router/children/ChildrenLifecycleTest.kt | 179 +++++++-- .../children/ChildrenRetainedInstanceTest.kt | 174 ++++++-- .../router/children/ChildrenSavedStateTest.kt | 372 +++++++++++++++--- .../DefaultCustomNavigationComponent.kt | 2 +- .../multipane/DefaultMultiPaneComponent.kt | 4 +- 14 files changed, 905 insertions(+), 289 deletions(-) diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index b03747386..527c95c24 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -88,9 +88,10 @@ public abstract interface class com/arkivanov/decompose/router/children/ChildNav } public final class com/arkivanov/decompose/router/children/ChildNavState$Status : java/lang/Enum { - public static final field ACTIVE Lcom/arkivanov/decompose/router/children/ChildNavState$Status; + public static final field CREATED Lcom/arkivanov/decompose/router/children/ChildNavState$Status; public static final field DESTROYED Lcom/arkivanov/decompose/router/children/ChildNavState$Status; - public static final field INACTIVE Lcom/arkivanov/decompose/router/children/ChildNavState$Status; + public static final field RESUMED Lcom/arkivanov/decompose/router/children/ChildNavState$Status; + public static final field STARTED Lcom/arkivanov/decompose/router/children/ChildNavState$Status; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/decompose/router/children/ChildNavState$Status; public static fun values ()[Lcom/arkivanov/decompose/router/children/ChildNavState$Status; diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api index 5d1632ca5..0129efa72 100644 --- a/decompose/api/jvm/decompose.api +++ b/decompose/api/jvm/decompose.api @@ -74,9 +74,10 @@ public abstract interface class com/arkivanov/decompose/router/children/ChildNav } public final class com/arkivanov/decompose/router/children/ChildNavState$Status : java/lang/Enum { - public static final field ACTIVE Lcom/arkivanov/decompose/router/children/ChildNavState$Status; + public static final field CREATED Lcom/arkivanov/decompose/router/children/ChildNavState$Status; public static final field DESTROYED Lcom/arkivanov/decompose/router/children/ChildNavState$Status; - public static final field INACTIVE Lcom/arkivanov/decompose/router/children/ChildNavState$Status; + public static final field RESUMED Lcom/arkivanov/decompose/router/children/ChildNavState$Status; + public static final field STARTED Lcom/arkivanov/decompose/router/children/ChildNavState$Status; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/decompose/router/children/ChildNavState$Status; public static fun values ()[Lcom/arkivanov/decompose/router/children/ChildNavState$Status; diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildNavState.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildNavState.kt index 58a03f605..f84447d05 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildNavState.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildNavState.kt @@ -21,20 +21,26 @@ interface ChildNavState { enum class Status { /** * The child component is destroyed but still managed, e.g. it's state may be saved and restored later. + * The state of the component is saved when it switches from any status to `DESTROYED`. */ DESTROYED, /** - * The child component is instantiated and inactive. Its maximum lifecycle state is `CREATED`, - * depending on the parent's lifecycle state. An inactive component cannot handle back button presses. + * The child component is instantiated and its maximum lifecycle state is `CREATED`, + * depending on the parent's lifecycle state. A `CREATED` component cannot handle back button presses. */ - INACTIVE, + CREATED, /** - * The child component is instantiated and active. Its maximum lifecycle state is `RESUMED`, - * depending on the parent's lifecycle state. An active component can handle back button presses. - * The state of the component is saved when it switches from `ACTIVE` to any other status. + * The child component is instantiated and its maximum lifecycle state is `STARTED`, + * depending on the parent's lifecycle state. A `STARTED` component can handle back button presses. */ - ACTIVE, + STARTED, + + /** + * The child component is instantiated and its maximum lifecycle state is `RESUMED`, + * depending on the parent's lifecycle state. A `RESUMED` component can handle back button presses. + */ + RESUMED, } } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt index 4c5d5e708..43f0320cb 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt @@ -2,13 +2,16 @@ package com.arkivanov.decompose.router.children import com.arkivanov.decompose.Child import com.arkivanov.decompose.router.children.ChildItem.Created +import com.arkivanov.decompose.router.children.ChildItem.Destroyed import com.arkivanov.decompose.router.children.ChildNavState.Status import com.arkivanov.essenty.instancekeeper.InstanceKeeper import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.create import com.arkivanov.essenty.lifecycle.destroy import com.arkivanov.essenty.lifecycle.doOnDestroy +import com.arkivanov.essenty.lifecycle.pause import com.arkivanov.essenty.lifecycle.resume +import com.arkivanov.essenty.lifecycle.start import com.arkivanov.essenty.lifecycle.stop import com.arkivanov.essenty.statekeeper.SerializableContainer @@ -54,7 +57,7 @@ internal class ChildrenNavigator>( item.lifecycleRegistry.destroy() } - is ChildItem.Destroyed -> Unit + is Destroyed -> Unit } } } @@ -66,41 +69,55 @@ internal class ChildrenNavigator>( navState.children.zip(savedStates).forEach { (childNavState, savedState) -> items += - when (childNavState.status) { - Status.DESTROYED -> ChildItem.Destroyed(configuration = childNavState.configuration, savedState = savedState) - - Status.INACTIVE -> { + restoreItem( + status = childNavState.status, + getDestroyedItem = { Destroyed(configuration = childNavState.configuration, savedState = savedState) }, + getCreatedItem = { childItemFactory( configuration = childNavState.configuration, savedState = savedState, instanceKeeperDispatcher = retainedChildren.remove(childNavState.configuration)?.instanceKeeperDispatcher, ).also { retainedInstance.items += it - it.lifecycleRegistry.create() } } - - Status.ACTIVE -> - childItemFactory( - configuration = childNavState.configuration, - savedState = savedState, - instanceKeeperDispatcher = retainedChildren.remove(childNavState.configuration)?.instanceKeeperDispatcher, - ).also { - it.backHandler.start() - retainedInstance.items += it - it.lifecycleRegistry.resume() - } - } + ) } retainedChildren.values.forEach { it.instanceKeeperDispatcher.destroy() } } + private inline fun restoreItem( + status: Status, + getDestroyedItem: () -> Destroyed, + getCreatedItem: () -> Created, + ): ChildItem = + when (status) { + Status.DESTROYED -> getDestroyedItem() + + Status.CREATED -> + getCreatedItem().apply { + lifecycleRegistry.create() + } + + Status.STARTED -> + getCreatedItem().apply { + backHandler.start() + lifecycleRegistry.start() + } + + Status.RESUMED -> + getCreatedItem().apply { + backHandler.start() + lifecycleRegistry.resume() + } + } + fun saveChildState(): List = items.map { item -> when (item) { is Created -> item.stateKeeperDispatcher.save() - is ChildItem.Destroyed -> item.savedState + is Destroyed -> item.savedState } } @@ -130,41 +147,27 @@ internal class ChildrenNavigator>( when (val child = oldItems[state.configuration]) { is Created -> child to state.status - is ChildItem.Destroyed -> + is Destroyed -> when (state.status) { Status.DESTROYED -> child to state.status - Status.INACTIVE -> + Status.CREATED, + Status.STARTED, + Status.RESUMED -> Pair( - first = childItemFactory( - configuration = state.configuration, - savedState = child.savedState - ).apply { lifecycleRegistry.create() }, - second = state.status, - ) - - Status.ACTIVE -> - Pair( - first = childItemFactory( - configuration = state.configuration, - savedState = child.savedState, - ).apply { lifecycleRegistry.create() }, + first = childItemFactory(configuration = state.configuration, savedState = child.savedState) + .apply { lifecycleRegistry.create() }, second = state.status, ) } null -> when (state.status) { - Status.DESTROYED -> ChildItem.Destroyed(configuration = state.configuration) to state.status + Status.DESTROYED -> Destroyed(configuration = state.configuration) to state.status - Status.INACTIVE -> - Pair( - first = childItemFactory(configuration = state.configuration) - .apply { lifecycleRegistry.create() }, - second = state.status, - ) - - Status.ACTIVE -> + Status.CREATED, + Status.STARTED, + Status.RESUMED -> Pair( first = childItemFactory(configuration = state.configuration) .apply { lifecycleRegistry.create() }, @@ -196,44 +199,59 @@ internal class ChildrenNavigator>( newItems.forEach { (item, status) -> items += when (item) { - is Created -> { - when (status) { - Status.DESTROYED -> { - val savedState = item.stateKeeperDispatcher.save() - item.destroy() - ChildItem.Destroyed(configuration = item.configuration, savedState = savedState) - } - - Status.INACTIVE -> { - retainedInstance.items += item - - item - .takeIf { it.lifecycleRegistry.state != Lifecycle.State.CREATED } - ?.apply { - backHandler.stop() - lifecycleRegistry.stop() - } - ?: item - } - - Status.ACTIVE -> { - retainedInstance.items += item - - item - .takeIf { it.lifecycleRegistry.state != Lifecycle.State.RESUMED } - ?.apply { - backHandler.start() - lifecycleRegistry.resume() - } - ?: item - } - } + is Created -> processNewItem(item, status) + is Destroyed -> item + } + } + } + + private fun processNewItem(item: Created, status: Status): ChildItem = + when (status) { + Status.DESTROYED -> { + val savedState = item.stateKeeperDispatcher.save() + item.destroy() + Destroyed(configuration = item.configuration, savedState = savedState) + } + + Status.CREATED -> { + retainedInstance.items += item + + if (item.lifecycleRegistry.state != Lifecycle.State.CREATED) { + item.backHandler.stop() + item.lifecycleRegistry.stop() + } + + item + } + + Status.STARTED -> { + retainedInstance.items += item + + when { + item.lifecycleRegistry.state < Lifecycle.State.STARTED -> { + item.backHandler.start() + item.lifecycleRegistry.start() } - is ChildItem.Destroyed -> item + item.lifecycleRegistry.state > Lifecycle.State.STARTED -> { + item.lifecycleRegistry.pause() + } + } + + item + } + + Status.RESUMED -> { + retainedInstance.items += item + + if (item.lifecycleRegistry.state != Lifecycle.State.RESUMED) { + item.backHandler.start() + item.lifecycleRegistry.resume() } + + item + } } - } private fun Created<*, *>.destroy() { backHandler.stop() diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt index 4c693e80d..cf7a437fb 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt @@ -25,8 +25,8 @@ import kotlinx.serialization.Serializable * @param key a key of the list, must be unique if there are multiple Child Pages used in * the same component. * @param pageStatus a function that returns a [Status] of a page at a given index. - * By default, the currently selected page is [Status.ACTIVE], its two neighbours - * are [Status.INACTIVE], and the rest are [Status.DESTROYED]. You can implement your own + * By default, the currently selected page is [Status.RESUMED], its two neighbours + * are [Status.CREATED], and the rest are [Status.DESTROYED]. You can implement your own * logic, for example with circular behaviour. * @param handleBackButton determines whether the previous component should be automatically * selected on back button press or not, default is `false`. @@ -94,8 +94,8 @@ private class SerializablePages( * @param key a key of the list, must be unique if there are multiple Child Pages used in * the same component. * @param pageStatus a function that returns a [Status] of a page at a given index. - * By default, the currently selected page is [Status.ACTIVE], its two neighbours - * are [Status.INACTIVE], and the rest are [Status.DESTROYED]. You can implement your own + * By default, the currently selected page is [Status.RESUMED], its two neighbours + * are [Status.CREATED], and the rest are [Status.DESTROYED]. You can implement your own * logic, for example with circular behaviour. * @param handleBackButton determines whether the previous component should be automatically * selected on back button press or not, default is `false`. @@ -158,8 +158,8 @@ fun ComponentContext.childPages( @PublishedApi internal fun getDefaultPageStatus(index: Int, pages: Pages<*>): Status = when (index) { - pages.selectedIndex -> Status.ACTIVE - in (pages.selectedIndex - 1)..(pages.selectedIndex + 1) -> Status.INACTIVE + pages.selectedIndex -> Status.RESUMED + in (pages.selectedIndex - 1)..(pages.selectedIndex + 1) -> Status.CREATED else -> Status.DESTROYED } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt index 3ddd61846..926a511f5 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt @@ -107,6 +107,6 @@ private data class SlotNavState( if (configuration == null) { emptyList() } else { - listOf(SimpleChildNavState(configuration = configuration, status = Status.ACTIVE)) + listOf(SimpleChildNavState(configuration = configuration, status = Status.RESUMED)) } } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt index 84e9a87e0..4480b09b1 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt @@ -143,7 +143,7 @@ private data class StackNavState( configurations.mapIndexed { index, configuration -> SimpleChildNavState( configuration = configuration, - status = if (index == configurations.lastIndex) Status.ACTIVE else Status.INACTIVE, + status = if (index == configurations.lastIndex) Status.RESUMED else Status.CREATED, ) } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt index 03ea6be9e..712799788 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt @@ -1,8 +1,9 @@ package com.arkivanov.decompose.router.children -import com.arkivanov.decompose.router.children.ChildNavState.Status.ACTIVE +import com.arkivanov.decompose.router.children.ChildNavState.Status.RESUMED import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED -import com.arkivanov.decompose.router.children.ChildNavState.Status.INACTIVE +import com.arkivanov.decompose.router.children.ChildNavState.Status.CREATED +import com.arkivanov.decompose.router.children.ChildNavState.Status.STARTED import com.arkivanov.decompose.value.getValue import com.arkivanov.essenty.backhandler.BackCallback import kotlin.test.Test @@ -14,9 +15,31 @@ import kotlin.test.assertTrue internal class ChildrenBackPressedTest : ChildrenTestBase() { @Test - fun GIVEN_children_created_with_active_child_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { + fun GIVEN_children_created_with_resumed_child_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 4).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + + backDispatcher.back() + + assertTrue(isCalled) + } + + @Test + fun GIVEN_children_created_with_resumed_child_and_disabled_callback_registered_WHEN_back_THEN_callback_not_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 4).requireInstance().backHandler.register(BackCallback(isEnabled = false) { isCalled = true }) + + backDispatcher.back() + + assertFalse(isCalled) + } + + @Test + fun GIVEN_children_created_with_started_child_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -25,9 +48,9 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { } @Test - fun GIVEN_children_created_with_active_child_and_disabled_callback_registered_WHEN_back_THEN_callback_not_called() { + fun GIVEN_children_created_with_started_child_and_disabled_callback_registered_WHEN_back_THEN_callback_not_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = false) { isCalled = true }) backDispatcher.back() @@ -36,9 +59,9 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { } @Test - fun GIVEN_children_created_with_inactive_child_and_enabled_callback_registered_WHEN_back_THEN_callback_not_called() { + fun GIVEN_children_created_with_created_child_and_enabled_callback_registered_WHEN_back_THEN_callback_not_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -47,9 +70,9 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { } @Test - fun GIVEN_children_created_with_inactive_child_and_disabled_callback_registered_WHEN_back_THEN_callback_not_called() { + fun GIVEN_children_created_with_created_child_and_disabled_callback_registered_WHEN_back_THEN_callback_not_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = false) { isCalled = true }) backDispatcher.back() @@ -58,10 +81,10 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { } @Test - fun GIVEN_child_switched_from_destroyed_to_inactive_and_enabled_callback_registered_WHEN_back_THEN_callback_not_called() { + fun GIVEN_child_switched_from_destroyed_to_created_and_enabled_callback_registered_WHEN_back_THEN_callback_not_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - navigate { listOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE) } + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + navigate { listOf(1 by CREATED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } children.getByConfig(config = 1).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -70,10 +93,22 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { } @Test - fun GIVEN_child_switched_from_destroyed_to_active_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { + fun GIVEN_child_switched_from_destroyed_to_started_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + navigate { listOf(1 by STARTED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } + children.getByConfig(config = 1).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + + backDispatcher.back() + + assertTrue(isCalled) + } + + @Test + fun GIVEN_child_switched_from_destroyed_to_resumed_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - navigate { listOf(1 by ACTIVE, 2 by INACTIVE, 3 by ACTIVE) } + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + navigate { listOf(1 by RESUMED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } children.getByConfig(config = 1).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -82,10 +117,22 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { } @Test - fun GIVEN_child_switched_from_inactive_to_active_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { + fun GIVEN_child_switched_from_created_to_started_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + navigate { listOf(1 by DESTROYED, 2 by STARTED, 3 by STARTED, 4 by RESUMED) } + children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + + backDispatcher.back() + + assertTrue(isCalled) + } + + @Test + fun GIVEN_child_switched_from_created_to_resumed_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) } + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + navigate { listOf(1 by DESTROYED, 2 by RESUMED, 3 by STARTED, 4 by RESUMED) } children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() @@ -94,23 +141,47 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { } @Test - fun GIVEN_child_switched_from_active_to_inactive_and_enabled_callback_registered_WHEN_back_THEN_callback_not_called() { + fun GIVEN_child_switched_from_started_to_resumed_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + navigate { listOf(1 by DESTROYED, 2 by RESUMED, 3 by RESUMED, 4 by RESUMED) } children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) backDispatcher.back() + assertTrue(isCalled) + } + + @Test + fun GIVEN_child_switched_from_resumed_to_started_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by STARTED) } + children.getByConfig(config = 4).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + + backDispatcher.back() + + assertTrue(isCalled) + } + + @Test + fun GIVEN_child_switched_from_resumed_to_created_and_enabled_callback_registered_WHEN_back_THEN_callback_not_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by CREATED) } + children.getByConfig(config = 4).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + + backDispatcher.back() + assertFalse(isCalled) } @Test - fun GIVEN_enabled_callback_registered_in_inactive_child_and_switched_to_active_WHEN_back_THEN_callback_called() { + fun GIVEN_enabled_callback_registered_in_created_child_and_switched_to_started_WHEN_back_THEN_callback_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) - navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by STARTED, 3 by STARTED, 4 by RESUMED) } backDispatcher.back() @@ -118,11 +189,23 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { } @Test - fun GIVEN_enabled_callback_registered_in_inactive_child_and_switched_to_destroyed_WHEN_back_THEN_callback_not_called() { + fun GIVEN_enabled_callback_registered_in_created_child_and_switched_to_resumed_WHEN_back_THEN_callback_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) - navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by RESUMED, 3 by STARTED, 4 by RESUMED) } + + backDispatcher.back() + + assertTrue(isCalled) + } + + @Test + fun GIVEN_enabled_callback_registered_in_created_child_and_switched_to_destroyed_WHEN_back_THEN_callback_not_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) } backDispatcher.back() @@ -130,11 +213,11 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { } @Test - fun GIVEN_enabled_callback_registered_in_active_child_and_switched_to_inactive_WHEN_back_THEN_callback_not_called() { + fun GIVEN_enabled_callback_registered_in_started_child_and_switched_to_created_WHEN_back_THEN_callback_not_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by CREATED, 4 by CREATED) } backDispatcher.back() @@ -142,11 +225,59 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { } @Test - fun GIVEN_enabled_callback_registered_in_active_child_and_switched_to_destroyed_WHEN_back_THEN_callback_not_called() { + fun GIVEN_enabled_callback_registered_in_started_child_and_switched_to_resumed_WHEN_back_THEN_callback_called() { var isCalled = false - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED, 4 by CREATED) } + + backDispatcher.back() + + assertTrue(isCalled) + } + + @Test + fun GIVEN_enabled_callback_registered_in_started_child_and_switched_to_destroyed_WHEN_back_THEN_callback_not_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by CREATED) } + + backDispatcher.back() + + assertFalse(isCalled) + } + + @Test + fun GIVEN_enabled_callback_registered_in_resumed_child_and_switched_to_created_WHEN_back_THEN_callback_not_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 4).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by CREATED) } + + backDispatcher.back() + + assertFalse(isCalled) + } + + @Test + fun GIVEN_enabled_callback_registered_in_resumed_child_and_switched_to_started_WHEN_back_THEN_callback_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 4).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by STARTED) } + + backDispatcher.back() + + assertTrue(isCalled) + } + + @Test + fun GIVEN_enabled_callback_registered_in_resumed_child_and_switched_to_destroyed_WHEN_back_THEN_callback_not_called() { + var isCalled = false + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 4).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true }) + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) } backDispatcher.back() @@ -156,7 +287,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { @Test fun GIVEN_backTransformer_returns_null_WHEN_back_THEN_returned_false() { context.children( - initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), + initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED), backTransformer = { null }, ) @@ -168,7 +299,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { @Test fun GIVEN_backTransformer_returns_not_null_WHEN_back_THEN_navigated() { val children by context.children( - initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), + initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED), backTransformer = { { stateOf() } }, ) @@ -180,7 +311,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { @Test fun GIVEN_navigated_and_backTransformer_returns_null_WHEN_back_THEN_returned_false() { context.children( - initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), + initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED), backTransformer = { state -> if (state.children.isNotEmpty()) { { stateOf() } @@ -200,7 +331,7 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { fun WHEN_back_THEN_onStateChanged_called() { val results = ArrayList>() context.children( - initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), + initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED), backTransformer = { { stateOf() } }, onStateChanged = { newState, oldState -> results += newState to oldState }, ) @@ -208,6 +339,6 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { backDispatcher.back() - assertContentEquals(listOf(stateOf() to stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)), results) + assertContentEquals(listOf(stateOf() to stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)), results) } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt index b8d0837eb..c1dc90a45 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt @@ -2,9 +2,9 @@ package com.arkivanov.decompose.router.children import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.router.TestInstance -import com.arkivanov.decompose.router.children.ChildNavState.Status.ACTIVE +import com.arkivanov.decompose.router.children.ChildNavState.Status.RESUMED import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED -import com.arkivanov.decompose.router.children.ChildNavState.Status.INACTIVE +import com.arkivanov.decompose.router.children.ChildNavState.Status.CREATED import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher import com.arkivanov.decompose.value.getValue import com.arkivanov.essenty.instancekeeper.getOrCreate @@ -20,13 +20,13 @@ internal class ChildrenBasicTest : ChildrenTestBase() { fun WHEN_navigate_THEN_onEventComplete_called() { val results = ArrayList>() context.children( - initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), + initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED), onEventComplete = { _, newState, oldState -> results += newState to oldState }, ) navigate { emptyList() } - assertContentEquals(listOf(stateOf() to stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)), results) + assertContentEquals(listOf(stateOf() to stateOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED)), results) } @Test @@ -34,32 +34,32 @@ internal class ChildrenBasicTest : ChildrenTestBase() { val results = ArrayList>() context.children( - initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), + initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED), onStateChanged = { newState, oldState -> results += newState to oldState }, ) - assertContentEquals(listOf(stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) to null), results) + assertContentEquals(listOf(stateOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED) to null), results) } @Test fun WHEN_navigate_THEN_onStateChanged_called() { val results = ArrayList>() context.children( - initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), + initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED), onStateChanged = { newState, oldState -> results += newState to oldState }, ) results.clear() navigate { emptyList() } - assertContentEquals(listOf(stateOf() to stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)), results) + assertContentEquals(listOf(stateOf() to stateOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED)), results) } @Test fun WHEN_navigate_THEN_onEventComplete_called_after_onStateChanged_called() { val results = ArrayList() context.children( - initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), + initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED), onEventComplete = { _, _, _ -> results += "onEventComplete" }, onStateChanged = { _, _ -> results += "onStateChanged" }, ) @@ -74,7 +74,7 @@ internal class ChildrenBasicTest : ChildrenTestBase() { fun WHEN_destroyed_THEN_components_destroyed_in_reversed_order() { val destroyEvents = ArrayList() - context.children(initialState = stateOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE)) { config, componentContext -> + context.children(initialState = stateOf(1 by CREATED, 2 by CREATED, 3 by RESUMED)) { config, componentContext -> componentContext.lifecycle.doOnDestroy { destroyEvents += config } Component(config = config, componentContext = componentContext) } @@ -88,7 +88,7 @@ internal class ChildrenBasicTest : ChildrenTestBase() { fun WHEN_created_THEN_components_created_in_order() { val createEvents = ArrayList() - context.children(initialState = stateOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE)) { config, componentContext -> + context.children(initialState = stateOf(1 by CREATED, 2 by CREATED, 3 by RESUMED)) { config, componentContext -> createEvents += config Component(config = config, componentContext = componentContext) } @@ -102,7 +102,7 @@ internal class ChildrenBasicTest : ChildrenTestBase() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - oldContext.children(initialState = stateOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE)) + oldContext.children(initialState = stateOf(1 by CREATED, 2 by CREATED, 3 by RESUMED)) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) @@ -116,29 +116,29 @@ internal class ChildrenBasicTest : ChildrenTestBase() { } @Test - fun GIVEN_active_child_with_retained_instance_WHEN_child_removed_THEN_component_destroyed_before_instance() { + fun GIVEN_resumed_child_with_retained_instance_WHEN_child_removed_THEN_component_destroyed_before_instance() { val destroyEvents = ArrayList() - val children by context.children(initialState = stateOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by CREATED, 2 by CREATED, 3 by RESUMED)) val component = children.getByConfig(config = 3).requireInstance() val instance = component.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) component.lifecycle.doOnDestroy { destroyEvents += "component" } instance.onDestroyed = { destroyEvents += "instance" } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED) } assertContentEquals(listOf("component", "instance"), destroyEvents) } @Test - fun GIVEN_active_child_with_retained_instance_WHEN_child_switched_to_destroyed_THEN_component_destroyed_before_instance() { + fun GIVEN_resumed_child_with_retained_instance_WHEN_child_switched_to_destroyed_THEN_component_destroyed_before_instance() { val destroyEvents = ArrayList() - val children by context.children(initialState = stateOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by CREATED, 2 by CREATED, 3 by RESUMED)) val component = children.getByConfig(config = 3).requireInstance() val instance = component.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) component.lifecycle.doOnDestroy { destroyEvents += "component" } instance.onDestroyed = { destroyEvents += "instance" } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED) } assertContentEquals(listOf("component", "instance"), destroyEvents) } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt index 115e4ac36..3b10394a1 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt @@ -1,8 +1,9 @@ package com.arkivanov.decompose.router.children -import com.arkivanov.decompose.router.children.ChildNavState.Status.ACTIVE +import com.arkivanov.decompose.router.children.ChildNavState.Status.RESUMED import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED -import com.arkivanov.decompose.router.children.ChildNavState.Status.INACTIVE +import com.arkivanov.decompose.router.children.ChildNavState.Status.CREATED +import com.arkivanov.decompose.router.children.ChildNavState.Status.STARTED import com.arkivanov.decompose.value.getValue import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.destroy @@ -15,17 +16,18 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { @Test fun WHEN_created_THEN_initial_state() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) - children.assertChildren(1 to null, 2 to 2, 3 to 3) + children.assertChildren(1 to null, 2 to 2, 3 to 3, 4 to 4) } @Test fun WHEN_created_THEN_lifecycles_correct() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 2).requireInstance().lifecycle.state) - assertEquals(Lifecycle.State.RESUMED, children.getByConfig(config = 3).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.STARTED, children.getByConfig(config = 3).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.RESUMED, children.getByConfig(config = 4).requireInstance().lifecycle.state) } @Test @@ -33,10 +35,11 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { val children by context.children() navigate { it + (1 by DESTROYED) } - navigate { it + (2 by INACTIVE) } - navigate { it + (3 by ACTIVE) } + navigate { it + (2 by CREATED) } + navigate { it + (3 by STARTED) } + navigate { it + (4 by RESUMED) } - children.assertChildren(1 to null, 2 to 2, 3 to 3) + children.assertChildren(1 to null, 2 to 2, 3 to 3, 4 to 4) } @Test @@ -44,34 +47,36 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { val children by context.children() navigate { it + (1 by DESTROYED) } - navigate { it + (2 by INACTIVE) } - navigate { it + (3 by ACTIVE) } + navigate { it + (2 by CREATED) } + navigate { it + (3 by STARTED) } + navigate { it + (4 by RESUMED) } assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 2).requireInstance().lifecycle.state) - assertEquals(Lifecycle.State.RESUMED, children.getByConfig(config = 3).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.STARTED, children.getByConfig(config = 3).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.RESUMED, children.getByConfig(config = 4).requireInstance().lifecycle.state) } @Test fun WHEN_destroyed_child_removed_THEN_state_updated() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) navigate { state -> state.filterNot { it.configuration == 1 } } - children.assertChildren(2 to 2, 3 to 3) + children.assertChildren(2 to 2, 3 to 3, 4 to 4) } @Test - fun WHEN_inactive_child_removed_THEN_state_updated() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_created_child_removed_THEN_state_updated() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) navigate { state -> state.filterNot { it.configuration == 2 } } - children.assertChildren(1 to null, 3 to 3) + children.assertChildren(1 to null, 3 to 3, 4 to 4) } @Test - fun WHEN_inactive_child_removed_THEN_lifecycle_destroyed() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_created_child_removed_THEN_lifecycle_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val component = children.getByConfig(config = 2).requireInstance() navigate { state -> state.filterNot { it.configuration == 2 } } @@ -80,17 +85,17 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { } @Test - fun WHEN_active_child_removed_THEN_state_updated() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_started_child_removed_THEN_state_updated() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) navigate { state -> state.filterNot { it.configuration == 3 } } - children.assertChildren(1 to null, 2 to 2) + children.assertChildren(1 to null, 2 to 2, 4 to 4) } @Test - fun WHEN_active_child_removed_THEN_lifecycle_destroyed() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_started_child_removed_THEN_lifecycle_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val component = children.getByConfig(config = 3).requireInstance() navigate { state -> state.filterNot { it.configuration == 3 } } @@ -98,9 +103,28 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state) } + @Test + fun WHEN_resumed_child_removed_THEN_state_updated() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + + navigate { state -> state.filterNot { it.configuration == 4 } } + + children.assertChildren(1 to null, 2 to 2, 3 to 3) + } + + @Test + fun WHEN_resumed_child_removed_THEN_lifecycle_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val component = children.getByConfig(config = 4).requireInstance() + + navigate { state -> state.filterNot { it.configuration == 4 } } + + assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state) + } + @Test fun WHEN_all_children_removed_THEN_state_updated() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) navigate { emptyList() } @@ -109,82 +133,151 @@ internal class ChildrenLifecycleTest : ChildrenTestBase() { @Test fun WHEN_all_children_removed_THEN_lifecycles_destroyed() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val component2 = children.getByConfig(config = 2).requireInstance() val component3 = children.getByConfig(config = 3).requireInstance() + val component4 = children.getByConfig(config = 4).requireInstance() navigate { emptyList() } assertEquals(Lifecycle.State.DESTROYED, component2.lifecycle.state) assertEquals(Lifecycle.State.DESTROYED, component3.lifecycle.state) + assertEquals(Lifecycle.State.DESTROYED, component4.lifecycle.state) } @Test - fun WHEN_child_switched_from_destroyed_to_inactive_THEN_lifecycle_created() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_destroyed_to_created_THEN_lifecycle_created() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) - navigate { listOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by CREATED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 1).requireInstance().lifecycle.state) } @Test - fun WHEN_child_switched_from_inactive_to_active_THEN_lifecycle_resumed() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_destroyed_to_started_THEN_lifecycle_started() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + + navigate { listOf(1 by STARTED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } + + assertEquals(Lifecycle.State.STARTED, children.getByConfig(config = 1).requireInstance().lifecycle.state) + } + + @Test + fun WHEN_child_switched_from_destroyed_to_resumed_THEN_lifecycle_resumed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) - navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by RESUMED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } + + assertEquals(Lifecycle.State.RESUMED, children.getByConfig(config = 1).requireInstance().lifecycle.state) + } + + @Test + fun WHEN_child_switched_from_created_to_started_THEN_lifecycle_started() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + + navigate { listOf(1 by CREATED, 2 by STARTED, 3 by STARTED, 4 by RESUMED) } + + assertEquals(Lifecycle.State.STARTED, children.getByConfig(config = 2).requireInstance().lifecycle.state) + } + + @Test + fun WHEN_child_switched_from_created_to_resumed_THEN_lifecycle_resumed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + + navigate { listOf(1 by CREATED, 2 by RESUMED, 3 by STARTED, 4 by RESUMED) } assertEquals(Lifecycle.State.RESUMED, children.getByConfig(config = 2).requireInstance().lifecycle.state) } @Test - fun WHEN_child_switched_from_inactive_to_destroyed_THEN_lifecycle_destroyed() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_started_to_resumed_THEN_lifecycle_resumed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED, 4 by RESUMED) } + + assertEquals(Lifecycle.State.RESUMED, children.getByConfig(config = 3).requireInstance().lifecycle.state) + } + + @Test + fun WHEN_child_switched_from_created_to_destroyed_THEN_lifecycle_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val component = children.getByConfig(config = 2).requireInstance() - navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) } assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state) } @Test - fun WHEN_child_switched_from_active_to_inactive_THEN_lifecycle_stopped() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_started_to_created_THEN_lifecycle_created() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by CREATED, 4 by RESUMED) } assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 3).requireInstance().lifecycle.state) } @Test - fun WHEN_child_switched_from_active_to_destroyed_THEN_lifecycle_destroyed() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_started_to_destroyed_THEN_lifecycle_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val component = children.getByConfig(config = 3).requireInstance() - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by RESUMED) } + + assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state) + } + + @Test + fun WHEN_child_switched_from_resumed_to_started_THEN_lifecycle_started() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by STARTED) } + + assertEquals(Lifecycle.State.STARTED, children.getByConfig(config = 4).requireInstance().lifecycle.state) + } + + @Test + fun WHEN_child_switched_from_resumed_to_created_THEN_lifecycle_created() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by CREATED) } + + assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 4).requireInstance().lifecycle.state) + } + + @Test + fun WHEN_child_switched_from_resumed_to_destroyed_THEN_lifecycle_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val component = children.getByConfig(config = 4).requireInstance() + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) } assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state) } @Test fun WHEN_context_lifecycle_stopped_THEN_all_component_lifecycles_stopped() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) lifecycle.stop() assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 2).requireInstance().lifecycle.state) assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 3).requireInstance().lifecycle.state) + assertEquals(Lifecycle.State.CREATED, children.getByConfig(config = 4).requireInstance().lifecycle.state) } @Test fun WHEN_context_lifecycle_destroyed_THEN_all_component_lifecycles_destroyed() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val component2 = children.getByConfig(config = 2).requireInstance() val component3 = children.getByConfig(config = 3).requireInstance() + val component4 = children.getByConfig(config = 4).requireInstance() lifecycle.destroy() assertEquals(Lifecycle.State.DESTROYED, component2.lifecycle.state) assertEquals(Lifecycle.State.DESTROYED, component3.lifecycle.state) + assertEquals(Lifecycle.State.DESTROYED, component4.lifecycle.state) } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt index 9e0cea576..28d420dd3 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt @@ -2,9 +2,10 @@ package com.arkivanov.decompose.router.children import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.router.TestInstance -import com.arkivanov.decompose.router.children.ChildNavState.Status.ACTIVE +import com.arkivanov.decompose.router.children.ChildNavState.Status.RESUMED import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED -import com.arkivanov.decompose.router.children.ChildNavState.Status.INACTIVE +import com.arkivanov.decompose.router.children.ChildNavState.Status.CREATED +import com.arkivanov.decompose.router.children.ChildNavState.Status.STARTED import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher import com.arkivanov.decompose.value.getValue import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher @@ -19,67 +20,107 @@ import kotlin.test.assertTrue internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { @Test - fun WHEN_child_switched_from_inactive_to_destroyed_THEN_instance_destroyed() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val component = children.getByConfig(config = 2).requireInstance() - val instance = component.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + fun WHEN_child_switched_from_created_to_destroyed_THEN_instance_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val instance = children.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) - navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) } assertTrue(instance.isDestroyed) } @Test - fun WHEN_child_switched_from_active_to_destroyed_THEN_instance_destroyed() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) - val component = children.getByConfig(config = 3).requireInstance() - val instance = component.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + fun WHEN_child_switched_from_started_to_destroyed_THEN_instance_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val instance = children.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by RESUMED) } assertTrue(instance.isDestroyed) } @Test - fun WHEN_child_switched_from_active_to_inactive_THEN_instance_not_destroyed() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_started_to_created_THEN_instance_not_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val instance = children.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by CREATED, 4 by RESUMED) } + + assertFalse(instance.isDestroyed) + } + + @Test + fun WHEN_child_switched_from_resumed_to_destroyed_THEN_instance_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val instance = children.getByConfig(config = 4).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) } + + assertTrue(instance.isDestroyed) + } + + @Test + fun WHEN_child_switched_from_resumed_to_created_THEN_instance_not_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val instance = children.getByConfig(config = 4).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by CREATED) } + + assertFalse(instance.isDestroyed) + } + + @Test + fun WHEN_child_switched_from_resumed_to_started_THEN_instance_not_destroyed() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val instance = children.getByConfig(config = 4).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by STARTED) } assertFalse(instance.isDestroyed) } @Test - fun GIVEN_child_switched_from_inactive_to_destroyed_WHEN_child_switched_to_inactive_THEN_instance_not_retained() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun GIVEN_child_switched_from_created_to_destroyed_WHEN_child_switched_to_created_THEN_instance_not_retained() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val oldInstance = children.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) - navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } val newInstance = children.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) assertNotSame(oldInstance, newInstance) } @Test - fun GIVEN_child_switched_from_active_to_destroyed_WHEN_child_switched_to_active_THEN_instance_not_retained() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun GIVEN_child_switched_from_started_to_destroyed_WHEN_child_switched_to_started_THEN_instance_not_retained() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val oldInstance = children.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by RESUMED) } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } val newInstance = children.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) assertNotSame(oldInstance, newInstance) } @Test - fun WHEN_inactive_child_recreated_THEN_instance_retained() { + fun GIVEN_child_switched_from_resumed_to_destroyed_WHEN_child_switched_to_resumed_THEN_instance_not_retained() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val oldInstance = children.getByConfig(config = 4).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) } + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } + val newInstance = children.getByConfig(config = 4).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + assertNotSame(oldInstance, newInstance) + } + + @Test + fun WHEN_created_child_recreated_THEN_instance_retained() { val oldStateKeeper = TestStateKeeperDispatcher() val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val oldInstance = oldChildren.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) val savedState = oldStateKeeper.save() @@ -92,11 +133,11 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { } @Test - fun WHEN_inactive_child_recreated_THEN_instance_not_destroyed() { + fun WHEN_created_child_recreated_THEN_instance_not_destroyed() { val oldStateKeeper = TestStateKeeperDispatcher() val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val instance = oldChildren.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) val savedState = oldStateKeeper.save() @@ -108,11 +149,11 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { } @Test - fun WHEN_active_child_recreated_THEN_instance_retained() { + fun WHEN_started_child_recreated_THEN_instance_retained() { val oldStateKeeper = TestStateKeeperDispatcher() val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val oldInstance = oldChildren.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) val savedState = oldStateKeeper.save() @@ -125,11 +166,11 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { } @Test - fun WHEN_active_child_recreated_THEN_instance_not_destroyed() { + fun WHEN_started_child_recreated_THEN_instance_not_destroyed() { val oldStateKeeper = TestStateKeeperDispatcher() val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val instance = oldChildren.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) val savedState = oldStateKeeper.save() @@ -141,17 +182,82 @@ internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { } @Test - fun WHEN_recreated_and_child_restored_as_destroyed_THEN_instance_destroyed() { + fun WHEN_resumed_child_recreated_THEN_instance_retained() { + val oldStateKeeper = TestStateKeeperDispatcher() + val instanceKeeper = InstanceKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val oldInstance = oldChildren.getByConfig(config = 4).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) + val newChildren by newContext.children() + val newInstance = newChildren.getByConfig(config = 4).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + assertSame(oldInstance, newInstance) + } + + @Test + fun WHEN_resumed_child_recreated_THEN_instance_not_destroyed() { + val oldStateKeeper = TestStateKeeperDispatcher() + val instanceKeeper = InstanceKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val instance = oldChildren.getByConfig(config = 4).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) + newContext.children() + + assertFalse(instance.isDestroyed) + } + + @Test + fun WHEN_recreated_and_created_child_restored_as_destroyed_THEN_instance_destroyed() { + val oldStateKeeper = TestStateKeeperDispatcher() + val instanceKeeper = InstanceKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val instance = oldChildren.getByConfig(config = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) + newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) }) + + assertTrue(instance.isDestroyed) + } + + @Test + fun WHEN_recreated_and_started_child_restored_as_destroyed_THEN_instance_destroyed() { val oldStateKeeper = TestStateKeeperDispatcher() val instanceKeeper = InstanceKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val instance = oldChildren.getByConfig(config = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) - newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }) + newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by RESUMED) }) + + assertTrue(instance.isDestroyed) + } + + @Test + fun WHEN_recreated_and_resumed_child_restored_as_destroyed_THEN_instance_destroyed() { + val oldStateKeeper = TestStateKeeperDispatcher() + val instanceKeeper = InstanceKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + val instance = oldChildren.getByConfig(config = 4).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper) + newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) }) assertTrue(instance.isDestroyed) } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt index a462f402d..55c2022ec 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt @@ -3,9 +3,10 @@ package com.arkivanov.decompose.router.children import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.consume import com.arkivanov.decompose.register -import com.arkivanov.decompose.router.children.ChildNavState.Status.ACTIVE +import com.arkivanov.decompose.router.children.ChildNavState.Status.RESUMED import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED -import com.arkivanov.decompose.router.children.ChildNavState.Status.INACTIVE +import com.arkivanov.decompose.router.children.ChildNavState.Status.CREATED +import com.arkivanov.decompose.router.children.ChildNavState.Status.STARTED import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher import com.arkivanov.decompose.value.getValue import kotlin.test.Test @@ -18,65 +19,93 @@ import kotlin.test.assertTrue internal class ChildrenSavedStateTest : ChildrenTestBase() { @Test - fun WHEN_child_switched_from_active_to_inactive_THEN_state_not_saved() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_resumed_to_started_THEN_state_not_saved() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) var isCalled = false - children.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { + children.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { isCalled = true "SavedState" } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by STARTED) } assertFalse(isCalled) } @Test - fun WHEN_child_switched_from_active_to_destroyed_THEN_state_saved() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_resumed_to_created_THEN_state_not_saved() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) var isCalled = false - children.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { + children.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { isCalled = true "SavedState" } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by CREATED) } + + assertFalse(isCalled) + } + + @Test + fun WHEN_child_switched_from_resumed_to_destroyed_THEN_state_saved() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + var isCalled = false + children.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { + isCalled = true + "SavedState" + } + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) } assertTrue(isCalled) } @Test - fun WHEN_child_switched_from_inactive_to_destroyed_THEN_state_saved() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_created_to_destroyed_THEN_state_saved() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) var isCalled = false children.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { isCalled = true "SavedState" } - navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) } assertTrue(isCalled) } @Test - fun WHEN_child_switched_from_inactive_to_active_THEN_state_not_saved() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_created_to_started_THEN_state_not_saved() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + var isCalled = false + children.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { + isCalled = true + "SavedState" + } + + navigate { listOf(1 by DESTROYED, 2 by STARTED, 3 by STARTED, 4 by RESUMED) } + + assertFalse(isCalled) + } + + @Test + fun WHEN_child_switched_from_created_to_resumed_THEN_state_not_saved() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) var isCalled = false children.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { isCalled = true "SavedState" } - navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by RESUMED, 3 by STARTED, 4 by RESUMED) } assertFalse(isCalled) } @Test - fun WHEN_child_switched_from_destroyed_to_inactive_THEN_state_not_saved() { + fun WHEN_child_switched_from_destroyed_to_created_THEN_state_not_saved() { var isCalled = false - context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) { config, componentContext -> + context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) { config, componentContext -> val component = Component(config, componentContext) if (config == 1) { component.stateKeeper.register(key = "key") { @@ -87,15 +116,15 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { component } - navigate { listOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by CREATED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } assertFalse(isCalled) } @Test - fun WHEN_child_switched_from_destroyed_to_active_THEN_state_not_saved() { + fun WHEN_child_switched_from_destroyed_to_started_THEN_state_not_saved() { var isCalled = false - context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) { config, componentContext -> + context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) { config, componentContext -> val component = Component(config, componentContext) if (config == 1) { component.stateKeeper.register(key = "key") { @@ -106,84 +135,188 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { component } - navigate { listOf(1 by ACTIVE, 2 by INACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by STARTED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } assertFalse(isCalled) } @Test - fun GIVEN_child_switched_from_active_to_destroyed_WHEN_child_switched_to_active_THEN_state_restored() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun WHEN_child_switched_from_destroyed_to_resumed_THEN_state_not_saved() { + var isCalled = false + context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) { config, componentContext -> + val component = Component(config, componentContext) + if (config == 1) { + component.stateKeeper.register(key = "key") { + isCalled = true + "SavedState" + } + } + component + } + + navigate { listOf(1 by RESUMED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } + + assertFalse(isCalled) + } + + @Test + fun GIVEN_child_switched_from_resumed_to_destroyed_WHEN_child_switched_to_resumed_THEN_state_restored() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { 40 } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) } + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } + val restoredState = children.getByConfig(config = 4).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(40, restoredState) + } + + @Test + fun GIVEN_child_switched_from_resumed_to_destroyed_WHEN_child_switched_to_started_THEN_state_restored() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { 40 } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) } + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by STARTED) } + val restoredState = children.getByConfig(config = 4).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(40, restoredState) + } + + @Test + fun GIVEN_child_switched_from_resumed_to_destroyed_WHEN_child_switched_to_created_THEN_state_restored() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { 40 } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) } + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by CREATED) } + val restoredState = children.getByConfig(config = 4).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(40, restoredState) + } + + @Test + fun GIVEN_child_switched_from_started_to_destroyed_WHEN_child_switched_to_resumed_THEN_state_restored() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by RESUMED) } + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED, 4 by RESUMED) } + val restoredState = children.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(30, restoredState) + } + + @Test + fun GIVEN_child_switched_from_started_to_destroyed_WHEN_child_switched_to_started_THEN_state_restored() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) children.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by RESUMED) } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } val restoredState = children.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") assertEquals(30, restoredState) } @Test - fun GIVEN_child_switched_from_active_to_destroyed_WHEN_child_switched_to_inactive_THEN_state_restored() { - val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + fun GIVEN_child_switched_from_started_to_destroyed_WHEN_child_switched_to_created_THEN_state_restored() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) children.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by RESUMED) } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by CREATED, 4 by RESUMED) } val restoredState = children.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") assertEquals(30, restoredState) } + @Test + fun GIVEN_child_switched_from_created_to_destroyed_WHEN_child_switched_to_resumed_THEN_state_restored() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } + navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) } + + navigate { listOf(1 by DESTROYED, 2 by RESUMED, 3 by STARTED, 4 by RESUMED) } + val restoredState = children.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(20, restoredState) + } + + @Test + fun GIVEN_child_switched_from_created_to_destroyed_WHEN_child_switched_to_started_THEN_state_restored() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } + navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) } + + navigate { listOf(1 by DESTROYED, 2 by STARTED, 3 by STARTED, 4 by RESUMED) } + val restoredState = children.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(20, restoredState) + } + + @Test + fun GIVEN_child_switched_from_created_to_destroyed_WHEN_child_switched_to_created_THEN_state_restored() { + val children by context.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + children.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } + navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) } + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } + val restoredState = children.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(20, restoredState) + } + @Test fun GIVEN_saving_state_WHEN_recreated_THEN_navigation_state_restored() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) val newChildren by newContext.children() - newChildren.assertChildren(1 to null, 2 to 2, 3 to 3) + newChildren.assertChildren(1 to null, 2 to 2, 3 to 3, 4 to 4) } @Test fun GIVEN_not_saving_state_WHEN_recreated_THEN_navigation_state_not_restored() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), saveState = { null }) + oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED), saveState = { null }) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) - val newChildren by newContext.children(initialState = stateOf(1 by ACTIVE)) + val newChildren by newContext.children(initialState = stateOf()) - newChildren.assertChildren(1 to 1) + newChildren.assertChildren() } @Test fun GIVEN_not_restoring_state_WHEN_recreated_THEN_navigation_state_not_restored() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) - val newChildren by newContext.children(initialState = stateOf(1 by ACTIVE), restoreState = { null }) + val newChildren by newContext.children(initialState = stateOf(), restoreState = { null }) - newChildren.assertChildren(1 to 1) + newChildren.assertChildren() } @Test fun GIVEN_saving_state_WHEN_recreated_THEN_child_states_restored() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } + oldChildren.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { 40 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) @@ -191,101 +324,228 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() { val newChildren by newContext.children() val restoredState2 = newChildren.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") + val restoredState4 = newChildren.getByConfig(config = 4).requireInstance().stateKeeper.consume(key = "key") assertEquals(20, restoredState2) assertEquals(30, restoredState3) + assertEquals(40, restoredState4) } @Test fun GIVEN_not_saving_state_WHEN_recreated_THEN_child_states_not_restored() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), saveState = { null }) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED), saveState = { null }) oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } + oldChildren.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { 40 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) - val newChildren by newContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val newChildren by newContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) val restoredState2 = newChildren.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") + val restoredState4 = newChildren.getByConfig(config = 4).requireInstance().stateKeeper.consume(key = "key") assertNull(restoredState2) assertNull(restoredState3) + assertNull(restoredState4) } @Test fun GIVEN_not_restoring_state_WHEN_recreated_THEN_child_states_not_restored() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } + oldChildren.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { 40 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) - val newChildren by newContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), restoreState = { null }) + val newChildren by newContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED), restoreState = { null }) val restoredState2 = newChildren.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") + val restoredState4 = newChildren.getByConfig(config = 4).requireInstance().stateKeeper.consume(key = "key") assertNull(restoredState2) assertNull(restoredState3) + assertNull(restoredState4) } @Test - fun GIVEN_recreated_and_child_restored_as_destroyed_WHEN_child_switched_to_inactive_THEN_state_restored() { + fun GIVEN_recreated_and_created_child_restored_as_destroyed_WHEN_child_switched_to_created_THEN_state_restored() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) + val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) }) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } + val restoredState3 = newChildren.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(20, restoredState3) + } + + @Test + fun GIVEN_recreated_and_created_child_restored_as_destroyed_WHEN_child_switched_to_started_THEN_state_restored() { + val oldStateKeeper = TestStateKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) + val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) }) + + navigate { listOf(1 by DESTROYED, 2 by STARTED, 3 by STARTED, 4 by RESUMED) } + val restoredState3 = newChildren.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(20, restoredState3) + } + + @Test + fun GIVEN_recreated_and_created_child_restored_as_destroyed_WHEN_child_switched_to_resumed_THEN_state_restored() { + val oldStateKeeper = TestStateKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 20 } + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) + val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by DESTROYED, 3 by STARTED, 4 by RESUMED) }) + + navigate { listOf(1 by DESTROYED, 2 by RESUMED, 3 by STARTED, 4 by RESUMED) } + val restoredState3 = newChildren.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(20, restoredState3) + } + + @Test + fun GIVEN_recreated_and_started_child_restored_as_destroyed_WHEN_child_switched_to_created_THEN_state_restored() { + val oldStateKeeper = TestStateKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) - val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }) + val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by RESUMED) }) - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by CREATED, 4 by RESUMED) } val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") assertEquals(30, restoredState3) } @Test - fun GIVEN_recreated_and_child_restored_as_destroyed_WHEN_child_switched_to_active_THEN_state_restored() { + fun GIVEN_recreated_and_started_child_restored_as_destroyed_WHEN_child_switched_to_started_THEN_state_restored() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } val savedState = oldStateKeeper.save() val newStateKeeper = TestStateKeeperDispatcher(savedState) val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) - val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }) + val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by RESUMED) }) - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") assertEquals(30, restoredState3) } @Test - fun GIVEN_child_switched_to_inactive_and_recreated_twice_WHEN_child_switched_to_active_THEN_state_restored() { + fun GIVEN_recreated_and_started_child_restored_as_destroyed_WHEN_child_switched_to_resumed_THEN_state_restored() { + val oldStateKeeper = TestStateKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + oldChildren.getByConfig(config = 3).requireInstance().stateKeeper.register(key = "key") { 30 } + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) + val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by CREATED, 3 by DESTROYED, 4 by RESUMED) }) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by RESUMED, 4 by RESUMED) } + val restoredState3 = newChildren.getByConfig(config = 3).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(30, restoredState3) + } + + @Test + fun GIVEN_recreated_and_resumed_child_restored_as_destroyed_WHEN_child_switched_to_created_THEN_state_restored() { + val oldStateKeeper = TestStateKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + oldChildren.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { 40 } + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) + val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) }) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by CREATED) } + val restoredState3 = newChildren.getByConfig(config = 4).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(40, restoredState3) + } + + @Test + fun GIVEN_recreated_and_resumed_child_restored_as_destroyed_WHEN_child_switched_to_started_THEN_state_restored() { + val oldStateKeeper = TestStateKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + oldChildren.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { 40 } + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) + val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) }) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by STARTED) } + val restoredState3 = newChildren.getByConfig(config = 4).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(40, restoredState3) + } + + @Test + fun GIVEN_recreated_and_resumed_child_restored_as_destroyed_WHEN_child_switched_to_resumed_THEN_state_restored() { + val oldStateKeeper = TestStateKeeperDispatcher() + val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)) + oldChildren.getByConfig(config = 4).requireInstance().stateKeeper.register(key = "key") { 40 } + val savedState = oldStateKeeper.save() + val newStateKeeper = TestStateKeeperDispatcher(savedState) + val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper) + val newChildren by newContext.children(restoreState = { stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by DESTROYED) }) + + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED) } + val restoredState3 = newChildren.getByConfig(config = 4).requireInstance().stateKeeper.consume(key = "key") + + assertEquals(40, restoredState3) + } + + @Test + fun GIVEN_child_switched_to_created_and_recreated_twice_WHEN_child_switched_to_resumed_THEN_state_restored() { val oldStateKeeper = TestStateKeeperDispatcher() val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper) - val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE)) + val oldChildren by oldContext.children(initialState = stateOf(1 by DESTROYED, 2 by RESUMED, 3 by STARTED)) oldChildren.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 30 } - navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) } + navigate { listOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED) } val savedState1 = oldStateKeeper.save() val newStateKeeper1 = TestStateKeeperDispatcher(savedState1) val newContext1 = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper1) - val newChildren1 by newContext1.children(restoreState = { stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) }) + val newChildren1 by newContext1.children(restoreState = { stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED) }) newChildren1.getByConfig(config = 2).requireInstance().stateKeeper.register(key = "key") { 31 } val savedState2 = newStateKeeper1.save() val newStateKeeper2 = TestStateKeeperDispatcher(savedState2) val newContext2 = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper2) - val newChildren2 by newContext2.children(restoreState = { stateOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) }) + val newChildren2 by newContext2.children(restoreState = { stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED) }) val restoredState2 = newChildren2.getByConfig(config = 2).requireInstance().stateKeeper.consume(key = "key") diff --git a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/DefaultCustomNavigationComponent.kt b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/DefaultCustomNavigationComponent.kt index 07897b816..4bbbbf3a1 100644 --- a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/DefaultCustomNavigationComponent.kt +++ b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/DefaultCustomNavigationComponent.kt @@ -103,7 +103,7 @@ class DefaultCustomNavigationComponent( configurations.mapIndexed { index, config -> SimpleChildNavState( configuration = config, - status = if (index == this.index) ChildNavState.Status.ACTIVE else ChildNavState.Status.INACTIVE, + status = if (index == this.index) ChildNavState.Status.RESUMED else ChildNavState.Status.CREATED, ) } } diff --git a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/DefaultMultiPaneComponent.kt b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/DefaultMultiPaneComponent.kt index e3ff541c4..71d29424c 100644 --- a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/DefaultMultiPaneComponent.kt +++ b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/DefaultMultiPaneComponent.kt @@ -92,8 +92,8 @@ internal class DefaultMultiPaneComponent( ) : NavState { override val children: List> by lazy { listOfNotNull( - SimpleChildNavState(Config.List, if (isMultiPane || (articleId == null)) Status.ACTIVE else Status.INACTIVE), - if (articleId != null) SimpleChildNavState(Config.Details(articleId), Status.ACTIVE) else null, + SimpleChildNavState(Config.List, if (isMultiPane || (articleId == null)) Status.RESUMED else Status.CREATED), + if (articleId != null) SimpleChildNavState(Config.Details(articleId), Status.RESUMED) else null, ) } } From 05dd939482f7aacdd2e7d53f76e0494c1d000ee3 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 7 Dec 2023 09:55:53 +0000 Subject: [PATCH 31/89] Renamed extensions-compose-jetbrains module to extensions-compose --- .../android/extensions-compose-jetbrains.api | 126 ---------------- .../api/jvm/extensions-compose-jetbrains.api | 138 ------------------ ...nov.decompose.mainthread.MainThreadChecker | 1 - .../.gitignore | 0 .../api/android/extensions-compose.api | 126 ++++++++++++++++ .../api/jvm/extensions-compose.api | 138 ++++++++++++++++++ .../build.gradle.kts | 2 +- .../src/androidMain/AndroidManifest.xml | 0 .../animation/predictiveback/LayoutCorners.kt | 2 +- .../extensions/compose}/SubscribeAsState.kt | 2 +- .../extensions/compose}/pages/Pages.kt | 4 +- .../compose}/pages/PagesScrollAnimation.kt | 2 +- .../extensions/compose}/stack/Children.kt | 8 +- .../stack/animation/AbstractStackAnimation.kt | 2 +- .../stack/animation/DefaultStackAnimator.kt | 2 +- .../compose}/stack/animation/Direction.kt | 2 +- .../stack/animation/EmptyStackAnimation.kt | 2 +- .../stack/animation/EmptyStackAnimator.kt | 2 +- .../compose}/stack/animation/Fade.kt | 2 +- .../stack/animation/MovableStackAnimation.kt | 2 +- .../compose}/stack/animation/Scale.kt | 2 +- .../stack/animation/SimpleStackAnimation.kt | 2 +- .../compose}/stack/animation/Slide.kt | 2 +- .../stack/animation/StackAnimation.kt | 2 +- .../compose}/stack/animation/StackAnimator.kt | 2 +- .../predictiveback/BackGestureHandler.kt | 2 +- .../DefaultPredictiveBackAnimatable.kt | 2 +- .../animation/predictiveback/LayoutCorners.kt | 2 +- .../MaterialPredictiveBackAnimatable.kt | 2 +- .../PredictiveBackAnimatable.kt | 2 +- .../predictiveback/PredictiveBackAnimation.kt | 6 +- .../extensions/compose}/utils/Icon.kt | 2 +- .../compose}/lifecycle/LifecycleController.kt | 2 +- .../mainthread/SwingMainThreadChecker.kt | 2 +- ...nov.decompose.mainthread.MainThreadChecker | 1 + .../lifecycle/LifecycleControllerTest.kt | 2 +- .../extensions/compose}/pages/PagesTest.kt | 2 +- .../extensions/compose}/stack/ChildrenTest.kt | 14 +- .../stack/animation/GetFadeAlphaTest.kt | 2 +- .../animation/StackAnimationDirectionsTest.kt | 10 +- .../compose}/PredictiveBackGestureIcon.kt | 4 +- .../compose}/PredictiveBackGestureOverlay.kt | 2 +- .../animation/predictiveback/LayoutCorners.kt | 2 +- sample/app-desktop/build.gradle.kts | 2 +- .../kotlin/com/arkivanov/sample/app/Main.kt | 2 +- sample/app-js-compose/build.gradle.kts | 2 +- sample/shared/compose/build.gradle.kts | 2 +- .../sample/shared/cards/CardsContent.kt | 6 +- .../sample/shared/cards/card/CardContent.kt | 5 +- .../sample/shared/counters/CountersContent.kt | 12 +- .../shared/counters/counter/CounterContent.kt | 2 +- .../CustomNavigationContent.kt | 3 +- .../shared/customnavigation/KittenContent.kt | 3 +- .../dynamicfeatures/DynamicFeaturesContent.kt | 6 +- .../dynamicfeature/DynamicFeatureContent.kt | 6 +- .../shared/multipane/MultiPaneContent.kt | 2 +- .../details/ArticleDetailsContent.kt | 2 +- .../multipane/list/ArticleListContent.kt | 2 +- .../sample/shared/root/RootContent.kt | 18 +-- .../sample/shared/root/RootViewController.kt | 4 +- settings.gradle.kts | 2 +- 61 files changed, 353 insertions(+), 362 deletions(-) delete mode 100644 extensions-compose-jetbrains/api/android/extensions-compose-jetbrains.api delete mode 100644 extensions-compose-jetbrains/api/jvm/extensions-compose-jetbrains.api delete mode 100644 extensions-compose-jetbrains/src/jvmMain/resources/META-INF/services/com.arkivanov.decompose.mainthread.MainThreadChecker rename {extensions-compose-jetbrains => extensions-compose}/.gitignore (100%) create mode 100644 extensions-compose/api/android/extensions-compose.api create mode 100644 extensions-compose/api/jvm/extensions-compose.api rename {extensions-compose-jetbrains => extensions-compose}/build.gradle.kts (95%) rename {extensions-compose-jetbrains => extensions-compose}/src/androidMain/AndroidManifest.xml (100%) rename {extensions-compose-jetbrains/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/predictiveback/LayoutCorners.kt (98%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/SubscribeAsState.kt (93%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/pages/Pages.kt (96%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/pages/PagesScrollAnimation.kt (86%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/Children.kt (85%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/AbstractStackAnimation.kt (98%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/DefaultStackAnimator.kt (95%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/Direction.kt (93%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/EmptyStackAnimation.kt (91%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/EmptyStackAnimator.kt (87%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/Fade.kt (91%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/MovableStackAnimation.kt (94%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/Scale.kt (91%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/SimpleStackAnimation.kt (92%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/Slide.kt (94%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/StackAnimation.kt (97%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/StackAnimator.kt (97%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/predictiveback/BackGestureHandler.kt (90%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt (93%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/predictiveback/LayoutCorners.kt (85%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt (98%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/predictiveback/PredictiveBackAnimatable.kt (96%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/predictiveback/PredictiveBackAnimation.kt (95%) rename {extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose}/utils/Icon.kt (95%) rename {extensions-compose-jetbrains/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose}/lifecycle/LifecycleController.kt (94%) rename {extensions-compose-jetbrains/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose}/mainthread/SwingMainThreadChecker.kt (77%) create mode 100644 extensions-compose/src/jvmMain/resources/META-INF/services/com.arkivanov.decompose.mainthread.MainThreadChecker rename {extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose}/lifecycle/LifecycleControllerTest.kt (97%) rename {extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose}/pages/PagesTest.kt (98%) rename {extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose}/stack/ChildrenTest.kt (91%) rename {extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/GetFadeAlphaTest.kt (97%) rename {extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/StackAnimationDirectionsTest.kt (91%) rename {extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose}/PredictiveBackGestureIcon.kt (88%) rename {extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose}/PredictiveBackGestureOverlay.kt (99%) rename {extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains => extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose}/stack/animation/predictiveback/LayoutCorners.kt (64%) diff --git a/extensions-compose-jetbrains/api/android/extensions-compose-jetbrains.api b/extensions-compose-jetbrains/api/android/extensions-compose-jetbrains.api deleted file mode 100644 index 3f7c68e9e..000000000 --- a/extensions-compose-jetbrains/api/android/extensions-compose-jetbrains.api +++ /dev/null @@ -1,126 +0,0 @@ -public final class com/arkivanov/decompose/extensions/compose/jetbrains/SubscribeAsStateKt { - public static final fun subscribeAsState (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/runtime/SnapshotMutationPolicy;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/pages/ComposableSingletons$PagesKt { - public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/jetbrains/pages/ComposableSingletons$PagesKt; - public static field lambda-1 Lkotlin/jvm/functions/Function6; - public static field lambda-2 Lkotlin/jvm/functions/Function6; - public fun ()V - public final fun getLambda-1$extensions_compose_jetbrains_release ()Lkotlin/jvm/functions/Function6; - public final fun getLambda-2$extensions_compose_jetbrains_release ()Lkotlin/jvm/functions/Function6; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesKt { - public static final fun Pages (Landroidx/compose/runtime/State;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V - public static final fun Pages (Lcom/arkivanov/decompose/value/Value;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V - public static final fun defaultHorizontalPager ()Lkotlin/jvm/functions/Function6; - public static final fun defaultVerticalPager ()Lkotlin/jvm/functions/Function6; -} - -public abstract interface class com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation { -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation$Custom : com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation { - public static final field $stable I - public fun (Landroidx/compose/animation/core/AnimationSpec;)V - public final fun getSpec ()Landroidx/compose/animation/core/AnimationSpec; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation$Default : com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation { - public static final field $stable I - public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation$Default; - public fun equals (Ljava/lang/Object;)Z - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation$Disabled : com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation { - public static final field $stable I - public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation$Disabled; - public fun equals (Ljava/lang/Object;)Z - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/ChildrenKt { - public static final fun Children (Lcom/arkivanov/decompose/router/stack/ChildStack;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V - public static final fun Children (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction : java/lang/Enum { - public static final field ENTER_BACK Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; - public static final field ENTER_FRONT Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; - public static final field EXIT_BACK Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; - public static final field EXIT_FRONT Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; - public static fun values ()[Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/DirectionKt { - public static final fun isBack (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction;)Z - public static final fun isEnter (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction;)Z - public static final fun isExit (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction;)Z - public static final fun isFront (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction;)Z -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/FadeKt { - public static final fun fade (Landroidx/compose/animation/core/FiniteAnimationSpec;F)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static synthetic fun fade$default (Landroidx/compose/animation/core/FiniteAnimationSpec;FILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/ScaleKt { - public static final fun scale (Landroidx/compose/animation/core/FiniteAnimationSpec;FF)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static synthetic fun scale$default (Landroidx/compose/animation/core/FiniteAnimationSpec;FFILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/SlideKt { - public static final fun slide (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static synthetic fun slide$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; -} - -public abstract interface class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation { - public abstract fun invoke (Lcom/arkivanov/decompose/router/stack/ChildStack;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimationKt { - public static final fun stackAnimation (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator;Z)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static final fun stackAnimation (ZLkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static final fun stackAnimation (ZLkotlin/jvm/functions/Function3;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static synthetic fun stackAnimation$default (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator;ZILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; -} - -public abstract interface class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator { - public abstract fun invoke (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimatorKt { - public static final fun plus (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static final fun stackAnimator (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static synthetic fun stackAnimator$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/MaterialPredictiveBackAnimatableKt { - public static final fun materialPredictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable; - public static synthetic fun materialPredictiveBackAnimatable$default (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable; -} - -public abstract interface class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable { - public abstract fun animate (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun finish (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun getEnterModifier ()Landroidx/compose/ui/Modifier; - public abstract fun getExitModifier ()Landroidx/compose/ui/Modifier; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatableKt { - public static final fun predictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimationKt { - public static final fun predictiveBackAnimation (Lcom/arkivanov/essenty/backhandler/BackHandler;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static synthetic fun predictiveBackAnimation$default (Lcom/arkivanov/essenty/backhandler/BackHandler;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; -} - diff --git a/extensions-compose-jetbrains/api/jvm/extensions-compose-jetbrains.api b/extensions-compose-jetbrains/api/jvm/extensions-compose-jetbrains.api deleted file mode 100644 index df381f2ee..000000000 --- a/extensions-compose-jetbrains/api/jvm/extensions-compose-jetbrains.api +++ /dev/null @@ -1,138 +0,0 @@ -public final class com/arkivanov/decompose/extensions/compose/jetbrains/PredictiveBackGestureIconKt { - public static final fun PredictiveBackGestureIcon-eaDK9VM (Landroidx/compose/ui/graphics/vector/ImageVector;FJJLandroidx/compose/runtime/Composer;II)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/PredictiveBackGestureOverlayKt { - public static final fun PredictiveBackGestureOverlay (Lcom/arkivanov/essenty/backhandler/BackDispatcher;Lkotlin/jvm/functions/Function4;Landroidx/compose/ui/Modifier;ZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/SubscribeAsStateKt { - public static final fun subscribeAsState (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/runtime/SnapshotMutationPolicy;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/lifecycle/LifecycleControllerKt { - public static final fun LifecycleController (Lcom/arkivanov/essenty/lifecycle/LifecycleRegistry;Landroidx/compose/ui/window/WindowState;Landroidx/compose/runtime/Composer;I)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/pages/ComposableSingletons$PagesKt { - public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/jetbrains/pages/ComposableSingletons$PagesKt; - public static field lambda-1 Lkotlin/jvm/functions/Function6; - public static field lambda-2 Lkotlin/jvm/functions/Function6; - public fun ()V - public final fun getLambda-1$extensions_compose_jetbrains ()Lkotlin/jvm/functions/Function6; - public final fun getLambda-2$extensions_compose_jetbrains ()Lkotlin/jvm/functions/Function6; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesKt { - public static final fun Pages (Landroidx/compose/runtime/State;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V - public static final fun Pages (Lcom/arkivanov/decompose/value/Value;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V - public static final fun defaultHorizontalPager ()Lkotlin/jvm/functions/Function6; - public static final fun defaultVerticalPager ()Lkotlin/jvm/functions/Function6; -} - -public abstract interface class com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation { -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation$Custom : com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation { - public static final field $stable I - public fun (Landroidx/compose/animation/core/AnimationSpec;)V - public final fun getSpec ()Landroidx/compose/animation/core/AnimationSpec; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation$Default : com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation { - public static final field $stable I - public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation$Default; - public fun equals (Ljava/lang/Object;)Z - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation$Disabled : com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation { - public static final field $stable I - public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation$Disabled; - public fun equals (Ljava/lang/Object;)Z - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/ChildrenKt { - public static final fun Children (Lcom/arkivanov/decompose/router/stack/ChildStack;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V - public static final fun Children (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction : java/lang/Enum { - public static final field ENTER_BACK Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; - public static final field ENTER_FRONT Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; - public static final field EXIT_BACK Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; - public static final field EXIT_FRONT Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; - public static fun values ()[Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/DirectionKt { - public static final fun isBack (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction;)Z - public static final fun isEnter (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction;)Z - public static final fun isExit (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction;)Z - public static final fun isFront (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction;)Z -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/FadeKt { - public static final fun fade (Landroidx/compose/animation/core/FiniteAnimationSpec;F)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static synthetic fun fade$default (Landroidx/compose/animation/core/FiniteAnimationSpec;FILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/ScaleKt { - public static final fun scale (Landroidx/compose/animation/core/FiniteAnimationSpec;FF)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static synthetic fun scale$default (Landroidx/compose/animation/core/FiniteAnimationSpec;FFILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/SlideKt { - public static final fun slide (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static synthetic fun slide$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; -} - -public abstract interface class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation { - public abstract fun invoke (Lcom/arkivanov/decompose/router/stack/ChildStack;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimationKt { - public static final fun stackAnimation (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator;Z)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static final fun stackAnimation (ZLkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static final fun stackAnimation (ZLkotlin/jvm/functions/Function3;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static synthetic fun stackAnimation$default (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator;ZILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; -} - -public abstract interface class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator { - public abstract fun invoke (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimatorKt { - public static final fun plus (Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static final fun stackAnimator (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; - public static synthetic fun stackAnimator$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/MaterialPredictiveBackAnimatableKt { - public static final fun materialPredictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable; - public static synthetic fun materialPredictiveBackAnimatable$default (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable; -} - -public abstract interface class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable { - public abstract fun animate (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun finish (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun getEnterModifier ()Landroidx/compose/ui/Modifier; - public abstract fun getExitModifier ()Landroidx/compose/ui/Modifier; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatableKt { - public static final fun predictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable; -} - -public final class com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimationKt { - public static final fun predictiveBackAnimation (Lcom/arkivanov/essenty/backhandler/BackHandler;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; - public static synthetic fun predictiveBackAnimation$default (Lcom/arkivanov/essenty/backhandler/BackHandler;Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation; -} - diff --git a/extensions-compose-jetbrains/src/jvmMain/resources/META-INF/services/com.arkivanov.decompose.mainthread.MainThreadChecker b/extensions-compose-jetbrains/src/jvmMain/resources/META-INF/services/com.arkivanov.decompose.mainthread.MainThreadChecker deleted file mode 100644 index d78029adb..000000000 --- a/extensions-compose-jetbrains/src/jvmMain/resources/META-INF/services/com.arkivanov.decompose.mainthread.MainThreadChecker +++ /dev/null @@ -1 +0,0 @@ -com.arkivanov.decompose.extensions.compose.jetbrains.mainthread.SwingMainThreadChecker diff --git a/extensions-compose-jetbrains/.gitignore b/extensions-compose/.gitignore similarity index 100% rename from extensions-compose-jetbrains/.gitignore rename to extensions-compose/.gitignore diff --git a/extensions-compose/api/android/extensions-compose.api b/extensions-compose/api/android/extensions-compose.api new file mode 100644 index 000000000..1ffa4dc63 --- /dev/null +++ b/extensions-compose/api/android/extensions-compose.api @@ -0,0 +1,126 @@ +public final class com/arkivanov/decompose/extensions/compose/SubscribeAsStateKt { + public static final fun subscribeAsState (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/runtime/SnapshotMutationPolicy;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; +} + +public final class com/arkivanov/decompose/extensions/compose/pages/ComposableSingletons$PagesKt { + public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/pages/ComposableSingletons$PagesKt; + public static field lambda-1 Lkotlin/jvm/functions/Function6; + public static field lambda-2 Lkotlin/jvm/functions/Function6; + public fun ()V + public final fun getLambda-1$extensions_compose_release ()Lkotlin/jvm/functions/Function6; + public final fun getLambda-2$extensions_compose_release ()Lkotlin/jvm/functions/Function6; +} + +public final class com/arkivanov/decompose/extensions/compose/pages/PagesKt { + public static final fun Pages (Landroidx/compose/runtime/State;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V + public static final fun Pages (Lcom/arkivanov/decompose/value/Value;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V + public static final fun defaultHorizontalPager ()Lkotlin/jvm/functions/Function6; + public static final fun defaultVerticalPager ()Lkotlin/jvm/functions/Function6; +} + +public abstract interface class com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation { +} + +public final class com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation$Custom : com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation { + public static final field $stable I + public fun (Landroidx/compose/animation/core/AnimationSpec;)V + public final fun getSpec ()Landroidx/compose/animation/core/AnimationSpec; +} + +public final class com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation$Default : com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation { + public static final field $stable I + public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation$Default; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation$Disabled : com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation { + public static final field $stable I + public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation$Disabled; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/ChildrenKt { + public static final fun Children (Lcom/arkivanov/decompose/router/stack/ChildStack;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V + public static final fun Children (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/Direction : java/lang/Enum { + public static final field ENTER_BACK Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; + public static final field ENTER_FRONT Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; + public static final field EXIT_BACK Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; + public static final field EXIT_FRONT Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; + public static fun values ()[Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/DirectionKt { + public static final fun isBack (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;)Z + public static final fun isEnter (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;)Z + public static final fun isExit (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;)Z + public static final fun isFront (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;)Z +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/FadeKt { + public static final fun fade (Landroidx/compose/animation/core/FiniteAnimationSpec;F)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; + public static synthetic fun fade$default (Landroidx/compose/animation/core/FiniteAnimationSpec;FILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/ScaleKt { + public static final fun scale (Landroidx/compose/animation/core/FiniteAnimationSpec;FF)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; + public static synthetic fun scale$default (Landroidx/compose/animation/core/FiniteAnimationSpec;FFILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/SlideKt { + public static final fun slide (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; + public static synthetic fun slide$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; +} + +public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation { + public abstract fun invoke (Lcom/arkivanov/decompose/router/stack/ChildStack;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationKt { + public static final fun stackAnimation (Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator;Z)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static final fun stackAnimation (ZLkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static final fun stackAnimation (ZLkotlin/jvm/functions/Function3;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static synthetic fun stackAnimation$default (Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator;ZILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; +} + +public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator { + public abstract fun invoke (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimatorKt { + public static final fun plus (Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; + public static final fun stackAnimator (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; + public static synthetic fun stackAnimator$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatableKt { + public static final fun materialPredictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; + public static synthetic fun materialPredictiveBackAnimatable$default (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; +} + +public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable { + public abstract fun animate (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun finish (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun getEnterModifier ()Landroidx/compose/ui/Modifier; + public abstract fun getExitModifier ()Landroidx/compose/ui/Modifier; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatableKt { + public static final fun predictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimationKt { + public static final fun predictiveBackAnimation (Lcom/arkivanov/essenty/backhandler/BackHandler;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static synthetic fun predictiveBackAnimation$default (Lcom/arkivanov/essenty/backhandler/BackHandler;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; +} + diff --git a/extensions-compose/api/jvm/extensions-compose.api b/extensions-compose/api/jvm/extensions-compose.api new file mode 100644 index 000000000..3bd776c26 --- /dev/null +++ b/extensions-compose/api/jvm/extensions-compose.api @@ -0,0 +1,138 @@ +public final class com/arkivanov/decompose/extensions/compose/PredictiveBackGestureIconKt { + public static final fun PredictiveBackGestureIcon-eaDK9VM (Landroidx/compose/ui/graphics/vector/ImageVector;FJJLandroidx/compose/runtime/Composer;II)V +} + +public final class com/arkivanov/decompose/extensions/compose/PredictiveBackGestureOverlayKt { + public static final fun PredictiveBackGestureOverlay (Lcom/arkivanov/essenty/backhandler/BackDispatcher;Lkotlin/jvm/functions/Function4;Landroidx/compose/ui/Modifier;ZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V +} + +public final class com/arkivanov/decompose/extensions/compose/SubscribeAsStateKt { + public static final fun subscribeAsState (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/runtime/SnapshotMutationPolicy;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; +} + +public final class com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleControllerKt { + public static final fun LifecycleController (Lcom/arkivanov/essenty/lifecycle/LifecycleRegistry;Landroidx/compose/ui/window/WindowState;Landroidx/compose/runtime/Composer;I)V +} + +public final class com/arkivanov/decompose/extensions/compose/pages/ComposableSingletons$PagesKt { + public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/pages/ComposableSingletons$PagesKt; + public static field lambda-1 Lkotlin/jvm/functions/Function6; + public static field lambda-2 Lkotlin/jvm/functions/Function6; + public fun ()V + public final fun getLambda-1$extensions_compose ()Lkotlin/jvm/functions/Function6; + public final fun getLambda-2$extensions_compose ()Lkotlin/jvm/functions/Function6; +} + +public final class com/arkivanov/decompose/extensions/compose/pages/PagesKt { + public static final fun Pages (Landroidx/compose/runtime/State;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V + public static final fun Pages (Lcom/arkivanov/decompose/value/Value;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V + public static final fun defaultHorizontalPager ()Lkotlin/jvm/functions/Function6; + public static final fun defaultVerticalPager ()Lkotlin/jvm/functions/Function6; +} + +public abstract interface class com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation { +} + +public final class com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation$Custom : com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation { + public static final field $stable I + public fun (Landroidx/compose/animation/core/AnimationSpec;)V + public final fun getSpec ()Landroidx/compose/animation/core/AnimationSpec; +} + +public final class com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation$Default : com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation { + public static final field $stable I + public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation$Default; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation$Disabled : com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation { + public static final field $stable I + public static final field INSTANCE Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation$Disabled; + public fun equals (Ljava/lang/Object;)Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/ChildrenKt { + public static final fun Children (Lcom/arkivanov/decompose/router/stack/ChildStack;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V + public static final fun Children (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/Direction : java/lang/Enum { + public static final field ENTER_BACK Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; + public static final field ENTER_FRONT Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; + public static final field EXIT_BACK Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; + public static final field EXIT_FRONT Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; + public static fun values ()[Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/DirectionKt { + public static final fun isBack (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;)Z + public static final fun isEnter (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;)Z + public static final fun isExit (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;)Z + public static final fun isFront (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;)Z +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/FadeKt { + public static final fun fade (Landroidx/compose/animation/core/FiniteAnimationSpec;F)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; + public static synthetic fun fade$default (Landroidx/compose/animation/core/FiniteAnimationSpec;FILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/ScaleKt { + public static final fun scale (Landroidx/compose/animation/core/FiniteAnimationSpec;FF)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; + public static synthetic fun scale$default (Landroidx/compose/animation/core/FiniteAnimationSpec;FFILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/SlideKt { + public static final fun slide (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; + public static synthetic fun slide$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Landroidx/compose/foundation/gestures/Orientation;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; +} + +public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation { + public abstract fun invoke (Lcom/arkivanov/decompose/router/stack/ChildStack;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationKt { + public static final fun stackAnimation (Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator;Z)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static final fun stackAnimation (ZLkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static final fun stackAnimation (ZLkotlin/jvm/functions/Function3;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static synthetic fun stackAnimation$default (Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator;ZILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; +} + +public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator { + public abstract fun invoke (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimatorKt { + public static final fun plus (Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; + public static final fun stackAnimator (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; + public static synthetic fun stackAnimator$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatableKt { + public static final fun materialPredictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; + public static synthetic fun materialPredictiveBackAnimatable$default (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; +} + +public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable { + public abstract fun animate (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun finish (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun getEnterModifier ()Landroidx/compose/ui/Modifier; + public abstract fun getExitModifier ()Landroidx/compose/ui/Modifier; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatableKt { + public static final fun predictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimationKt { + public static final fun predictiveBackAnimation (Lcom/arkivanov/essenty/backhandler/BackHandler;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; + public static synthetic fun predictiveBackAnimation$default (Lcom/arkivanov/essenty/backhandler/BackHandler;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; +} + diff --git a/extensions-compose-jetbrains/build.gradle.kts b/extensions-compose/build.gradle.kts similarity index 95% rename from extensions-compose-jetbrains/build.gradle.kts rename to extensions-compose/build.gradle.kts index c79b232b8..0501e8134 100644 --- a/extensions-compose-jetbrains/build.gradle.kts +++ b/extensions-compose/build.gradle.kts @@ -26,7 +26,7 @@ setupPublication() setupBinaryCompatibilityValidator() android { - namespace = "com.arkivanov.decompose.extensions.compose.jetbrains" + namespace = "com.arkivanov.decompose.extensions.compose" } kotlin { diff --git a/extensions-compose-jetbrains/src/androidMain/AndroidManifest.xml b/extensions-compose/src/androidMain/AndroidManifest.xml similarity index 100% rename from extensions-compose-jetbrains/src/androidMain/AndroidManifest.xml rename to extensions-compose/src/androidMain/AndroidManifest.xml diff --git a/extensions-compose-jetbrains/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/LayoutCorners.kt b/extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt similarity index 98% rename from extensions-compose-jetbrains/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/LayoutCorners.kt rename to extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt index a0711ae3f..4b75e1466 100644 --- a/extensions-compose-jetbrains/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/LayoutCorners.kt +++ b/extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.predictiveback +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import android.content.Context import android.os.Build diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/SubscribeAsState.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/SubscribeAsState.kt similarity index 93% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/SubscribeAsState.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/SubscribeAsState.kt index 99856df1d..8be08d90d 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/SubscribeAsState.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/SubscribeAsState.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains +package com.arkivanov.decompose.extensions.compose import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/pages/Pages.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/pages/Pages.kt similarity index 96% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/pages/Pages.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/pages/Pages.kt index 1d71213a0..48d953aa5 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/pages/Pages.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/pages/Pages.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.pages +package com.arkivanov.decompose.extensions.compose.pages import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.pager.HorizontalPager @@ -16,7 +16,7 @@ import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.Ref -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.hashString import com.arkivanov.decompose.router.pages.ChildPages import com.arkivanov.decompose.value.Value diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation.kt similarity index 86% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation.kt index 9aba9b04c..765641f17 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesScrollAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.pages +package com.arkivanov.decompose.extensions.compose.pages import androidx.compose.animation.core.AnimationSpec import androidx.compose.foundation.ExperimentalFoundationApi diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/Children.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt similarity index 85% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/Children.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt index 5a5845053..a22e32218 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/Children.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack +package com.arkivanov.decompose.extensions.compose.stack import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -7,9 +7,9 @@ import androidx.compose.runtime.saveable.SaveableStateHolder import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.StackAnimation -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.emptyStackAnimation -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.emptyStackAnimation +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.hashString import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.value.Value diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/AbstractStackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/AbstractStackAnimation.kt similarity index 98% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/AbstractStackAnimation.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/AbstractStackAnimation.kt index 8bb8c5426..b725fbb52 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/AbstractStackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/AbstractStackAnimation.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/DefaultStackAnimator.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/DefaultStackAnimator.kt similarity index 95% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/DefaultStackAnimator.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/DefaultStackAnimator.kt index e6ac4d5df..b88374fe2 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/DefaultStackAnimator.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/DefaultStackAnimator.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.animation.core.AnimationState import androidx.compose.animation.core.FiniteAnimationSpec diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Direction.kt similarity index 93% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Direction.kt index a35067101..da9374ff6 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Direction.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Direction.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation /** * Represents a direction in which child widgets are animated. diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/EmptyStackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt similarity index 91% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/EmptyStackAnimation.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt index 4220c05ef..55cfc2714 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/EmptyStackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/EmptyStackAnimator.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimator.kt similarity index 87% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/EmptyStackAnimator.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimator.kt index 96eb68f1f..2e4a3d178 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/EmptyStackAnimator.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimator.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Fade.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Fade.kt similarity index 91% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Fade.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Fade.kt index 298230931..e9d4b5e38 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Fade.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Fade.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.tween diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/MovableStackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/MovableStackAnimation.kt similarity index 94% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/MovableStackAnimation.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/MovableStackAnimation.kt index 654d36bf7..f13db480f 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/MovableStackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/MovableStackAnimation.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Scale.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Scale.kt similarity index 91% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Scale.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Scale.kt index 31fa790ea..53581bed7 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Scale.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Scale.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.tween diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/SimpleStackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/SimpleStackAnimation.kt similarity index 92% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/SimpleStackAnimation.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/SimpleStackAnimation.kt index d1fee07e0..804142335 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/SimpleStackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/SimpleStackAnimation.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Slide.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Slide.kt similarity index 94% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Slide.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Slide.kt index f6bb10182..790f75627 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/Slide.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/Slide.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.tween diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation.kt similarity index 97% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation.kt index c2717ff9b..f860a30ef 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator.kt similarity index 97% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator.kt index d7943c6b4..976b39749 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimator.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.tween diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/BackGestureHandler.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/BackGestureHandler.kt similarity index 90% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/BackGestureHandler.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/BackGestureHandler.kt index b85a5688a..f58cbcf8e 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/BackGestureHandler.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/BackGestureHandler.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.predictiveback +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt similarity index 93% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt index 11fe9beef..0bd551520 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.predictiveback +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import androidx.compose.animation.core.Animatable import androidx.compose.runtime.getValue diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/LayoutCorners.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt similarity index 85% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/LayoutCorners.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt index 9a1ef6122..8a95c7c3a 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/LayoutCorners.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.predictiveback +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt similarity index 98% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt index 967cfa53d..3fb1ad834 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.predictiveback +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import androidx.compose.animation.core.Animatable import androidx.compose.foundation.shape.RoundedCornerShape diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable.kt similarity index 96% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable.kt index 874305104..4e7da8a8d 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimatable.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.predictiveback +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import androidx.compose.ui.Modifier import com.arkivanov.decompose.ExperimentalDecomposeApi diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt similarity index 95% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimation.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt index 3b46cd641..808b51fe5 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/PredictiveBackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.predictiveback +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -15,8 +15,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.StackAnimation -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.emptyStackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.emptyStackAnimation import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.essenty.backhandler.BackEvent import com.arkivanov.essenty.backhandler.BackHandler diff --git a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/utils/Icon.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/utils/Icon.kt similarity index 95% rename from extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/utils/Icon.kt rename to extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/utils/Icon.kt index 61ed4de69..3818ba45a 100644 --- a/extensions-compose-jetbrains/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/utils/Icon.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/utils/Icon.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.utils +package com.arkivanov.decompose.extensions.compose.utils import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size diff --git a/extensions-compose-jetbrains/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/lifecycle/LifecycleController.kt b/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt similarity index 94% rename from extensions-compose-jetbrains/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/lifecycle/LifecycleController.kt rename to extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt index 985f19b55..35f56f8e3 100644 --- a/extensions-compose-jetbrains/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/lifecycle/LifecycleController.kt +++ b/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.lifecycle +package com.arkivanov.decompose.extensions.compose.lifecycle import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect diff --git a/extensions-compose-jetbrains/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/mainthread/SwingMainThreadChecker.kt b/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/mainthread/SwingMainThreadChecker.kt similarity index 77% rename from extensions-compose-jetbrains/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/mainthread/SwingMainThreadChecker.kt rename to extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/mainthread/SwingMainThreadChecker.kt index 4945ff9be..79d893e52 100644 --- a/extensions-compose-jetbrains/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/mainthread/SwingMainThreadChecker.kt +++ b/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/mainthread/SwingMainThreadChecker.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.mainthread +package com.arkivanov.decompose.extensions.compose.mainthread import com.arkivanov.decompose.mainthread.MainThreadChecker import javax.swing.SwingUtilities diff --git a/extensions-compose/src/jvmMain/resources/META-INF/services/com.arkivanov.decompose.mainthread.MainThreadChecker b/extensions-compose/src/jvmMain/resources/META-INF/services/com.arkivanov.decompose.mainthread.MainThreadChecker new file mode 100644 index 000000000..1818544ea --- /dev/null +++ b/extensions-compose/src/jvmMain/resources/META-INF/services/com.arkivanov.decompose.mainthread.MainThreadChecker @@ -0,0 +1 @@ +com.arkivanov.decompose.extensions.compose.mainthread.SwingMainThreadChecker diff --git a/extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/lifecycle/LifecycleControllerTest.kt b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleControllerTest.kt similarity index 97% rename from extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/lifecycle/LifecycleControllerTest.kt rename to extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleControllerTest.kt index fe9afed74..5644b6612 100644 --- a/extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/lifecycle/LifecycleControllerTest.kt +++ b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleControllerTest.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.lifecycle +package com.arkivanov.decompose.extensions.compose.lifecycle import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue diff --git a/extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesTest.kt b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/pages/PagesTest.kt similarity index 98% rename from extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesTest.kt rename to extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/pages/PagesTest.kt index 8d3fdd389..84824c729 100644 --- a/extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/pages/PagesTest.kt +++ b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/pages/PagesTest.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.pages +package com.arkivanov.decompose.extensions.compose.pages import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.text.BasicText diff --git a/extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/ChildrenTest.kt b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/stack/ChildrenTest.kt similarity index 91% rename from extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/ChildrenTest.kt rename to extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/stack/ChildrenTest.kt index c9623b4f3..64fb22877 100644 --- a/extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/ChildrenTest.kt +++ b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/stack/ChildrenTest.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack +package com.arkivanov.decompose.extensions.compose.stack import androidx.compose.foundation.clickable import androidx.compose.foundation.text.BasicText @@ -14,12 +14,12 @@ import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import com.arkivanov.decompose.Child -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.StackAnimation -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.fade -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.plus -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.scale -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.slide -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.fade +import com.arkivanov.decompose.extensions.compose.stack.animation.plus +import com.arkivanov.decompose.extensions.compose.stack.animation.scale +import com.arkivanov.decompose.extensions.compose.stack.animation.slide +import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.decompose.router.stack.ChildStack import org.junit.Rule import org.junit.Test diff --git a/extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/GetFadeAlphaTest.kt b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/GetFadeAlphaTest.kt similarity index 97% rename from extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/GetFadeAlphaTest.kt rename to extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/GetFadeAlphaTest.kt index 3e8f6c120..80c9877e5 100644 --- a/extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/GetFadeAlphaTest.kt +++ b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/GetFadeAlphaTest.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import kotlin.test.Test import kotlin.test.assertEquals diff --git a/extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimationDirectionsTest.kt b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationDirectionsTest.kt similarity index 91% rename from extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimationDirectionsTest.kt rename to extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationDirectionsTest.kt index 6095f5ad9..0f8290965 100644 --- a/extensions-compose-jetbrains/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/StackAnimationDirectionsTest.kt +++ b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationDirectionsTest.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation +package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue @@ -7,10 +7,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.test.junit4.createComposeRule import com.arkivanov.decompose.Child -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.Direction.ENTER_BACK -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.Direction.ENTER_FRONT -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.Direction.EXIT_BACK -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.Direction.EXIT_FRONT +import com.arkivanov.decompose.extensions.compose.stack.animation.Direction.ENTER_BACK +import com.arkivanov.decompose.extensions.compose.stack.animation.Direction.ENTER_FRONT +import com.arkivanov.decompose.extensions.compose.stack.animation.Direction.EXIT_BACK +import com.arkivanov.decompose.extensions.compose.stack.animation.Direction.EXIT_FRONT import com.arkivanov.decompose.router.stack.ChildStack import org.junit.Rule import org.junit.runner.RunWith diff --git a/extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/PredictiveBackGestureIcon.kt b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureIcon.kt similarity index 88% rename from extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/PredictiveBackGestureIcon.kt rename to extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureIcon.kt index 6524a8509..e2a75a6a5 100644 --- a/extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/PredictiveBackGestureIcon.kt +++ b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureIcon.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains +package com.arkivanov.decompose.extensions.compose import androidx.compose.foundation.background import androidx.compose.foundation.layout.padding @@ -11,7 +11,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.unit.dp import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.decompose.extensions.compose.jetbrains.utils.IconCompat +import com.arkivanov.decompose.extensions.compose.utils.IconCompat /** * Default icon for predictive back gesture. diff --git a/extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/PredictiveBackGestureOverlay.kt b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureOverlay.kt similarity index 99% rename from extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/PredictiveBackGestureOverlay.kt rename to extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureOverlay.kt index b315842e8..2800ad614 100644 --- a/extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/PredictiveBackGestureOverlay.kt +++ b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureOverlay.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains +package com.arkivanov.decompose.extensions.compose import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn diff --git a/extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/LayoutCorners.kt b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt similarity index 64% rename from extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/LayoutCorners.kt rename to extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt index ee14e7b17..2c4a65f8f 100644 --- a/extensions-compose-jetbrains/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/jetbrains/stack/animation/predictiveback/LayoutCorners.kt +++ b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.predictiveback +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import androidx.compose.ui.Modifier diff --git a/sample/app-desktop/build.gradle.kts b/sample/app-desktop/build.gradle.kts index b5c07a3a0..f47b0eb02 100644 --- a/sample/app-desktop/build.gradle.kts +++ b/sample/app-desktop/build.gradle.kts @@ -21,7 +21,7 @@ kotlin { jvm.main.dependencies { implementation(project(":decompose")) - implementation(project(":extensions-compose-jetbrains")) + implementation(project(":extensions-compose")) implementation(project(":sample:shared:shared")) implementation(project(":sample:shared:compose")) implementation(compose.desktop.currentOs) diff --git a/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt b/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt index 2a1b7bc7d..48ffaecf2 100644 --- a/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt +++ b/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.window.application import androidx.compose.ui.window.rememberWindowState import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.decompose.extensions.compose.jetbrains.lifecycle.LifecycleController +import com.arkivanov.decompose.extensions.compose.lifecycle.LifecycleController import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher import com.arkivanov.sample.shared.dynamicfeatures.dynamicfeature.DefaultFeatureInstaller diff --git a/sample/app-js-compose/build.gradle.kts b/sample/app-js-compose/build.gradle.kts index d01c56a48..91f3ba4fa 100644 --- a/sample/app-js-compose/build.gradle.kts +++ b/sample/app-js-compose/build.gradle.kts @@ -21,7 +21,7 @@ kotlin { js.main.dependencies { implementation(project(":decompose")) - implementation(project(":extensions-compose-jetbrains")) + implementation(project(":extensions-compose")) implementation(project(":sample:shared:shared")) implementation(project(":sample:shared:compose")) implementation(compose.runtime) diff --git a/sample/shared/compose/build.gradle.kts b/sample/shared/compose/build.gradle.kts index 1a8a35169..ee1b581ef 100644 --- a/sample/shared/compose/build.gradle.kts +++ b/sample/shared/compose/build.gradle.kts @@ -53,7 +53,7 @@ kotlin { common.main.dependencies { api(project(":decompose")) - implementation(project(":extensions-compose-jetbrains")) + implementation(project(":extensions-compose")) api(project(":sample:shared:shared")) implementation(project(":sample:shared:dynamic-features:api")) implementation(project(":sample:shared:dynamic-features:compose-api")) diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/cards/CardsContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/cards/CardsContent.kt index 6ed0af223..79f82e740 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/cards/CardsContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/cards/CardsContent.kt @@ -46,13 +46,9 @@ import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.round import com.arkivanov.decompose.Child -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState -import com.arkivanov.decompose.router.stack.ChildStack -import com.arkivanov.decompose.value.MutableValue -import com.arkivanov.decompose.value.Value +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.sample.shared.cards.card.CardComponent import com.arkivanov.sample.shared.cards.card.CardContent -import com.arkivanov.sample.shared.cards.card.PreviewCardComponent import com.arkivanov.sample.shared.utils.toPx @Composable diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/cards/card/CardContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/cards/card/CardContent.kt index 2b4fa020c..716a54d89 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/cards/card/CardContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/cards/card/CardContent.kt @@ -20,10 +20,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState -import com.arkivanov.decompose.value.MutableValue -import com.arkivanov.decompose.value.Value -import com.arkivanov.sample.shared.cards.card.CardComponent.Model +import com.arkivanov.decompose.extensions.compose.subscribeAsState @Composable internal fun CardContent(component: CardComponent, modifier: Modifier = Modifier) { diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt index 062214173..2574f68f3 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt @@ -10,12 +10,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.FaultyDecomposeApi -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.Children -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.fade -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.plus -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.predictiveback.predictiveBackAnimation -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.scale -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimation +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.stack.animation.fade +import com.arkivanov.decompose.extensions.compose.stack.animation.plus +import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.predictiveBackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.scale +import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.sample.shared.counters.counter.CounterContent @OptIn(ExperimentalDecomposeApi::class, FaultyDecomposeApi::class) diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt index 678b092e1..31cca2a24 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/CounterContent.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.sample.shared.dialog.DialogContent @Composable diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/CustomNavigationContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/CustomNavigationContent.kt index e9cdff2f4..bb228d211 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/CustomNavigationContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/CustomNavigationContent.kt @@ -28,8 +28,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.sample.shared.customnavigation.CustomNavigationComponent.Children import com.arkivanov.sample.shared.customnavigation.CustomNavigationComponent.Mode import com.arkivanov.sample.shared.utils.Degrees diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.kt index d5ba2e534..f10da1ce2 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/customnavigation/KittenContent.kt @@ -19,9 +19,8 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.extensions.compose.subscribeAsState @Composable internal fun KittenContent( diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/DynamicFeaturesContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/DynamicFeaturesContent.kt index cfcc3df31..7cc81d1ba 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/DynamicFeaturesContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/DynamicFeaturesContent.kt @@ -3,9 +3,9 @@ package com.arkivanov.sample.shared.dynamicfeatures import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.Children -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.fade -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimation +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.stack.animation.fade +import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.sample.shared.dynamicfeatures.DynamicFeaturesComponent.Child.Feature1Child import com.arkivanov.sample.shared.dynamicfeatures.DynamicFeaturesComponent.Child.Feature2Child import com.arkivanov.sample.shared.dynamicfeatures.dynamicfeature.DynamicFeatureContent diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/dynamicfeature/DynamicFeatureContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/dynamicfeature/DynamicFeatureContent.kt index ea0b0859d..217ee098d 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/dynamicfeature/DynamicFeatureContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dynamicfeatures/dynamicfeature/DynamicFeatureContent.kt @@ -7,9 +7,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.Children -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.fade -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimation +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.stack.animation.fade +import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.sample.shared.dynamicfeatures.DynamicFeatureContent import com.arkivanov.sample.shared.dynamicfeatures.dynamicfeature.DynamicFeatureComponent.Child.ErrorChild import com.arkivanov.sample.shared.dynamicfeatures.dynamicfeature.DynamicFeatureComponent.Child.FeatureChild diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/MultiPaneContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/MultiPaneContent.kt index 98524d38d..e74f384a8 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/MultiPaneContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/MultiPaneContent.kt @@ -17,7 +17,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.arkivanov.decompose.Child -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.sample.shared.multipane.details.ArticleDetailsComponent import com.arkivanov.sample.shared.multipane.details.ArticleDetailsContent import com.arkivanov.sample.shared.multipane.list.ArticleListComponent diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/details/ArticleDetailsContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/details/ArticleDetailsContent.kt index 42f9b293d..9a5379ac4 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/details/ArticleDetailsContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/details/ArticleDetailsContent.kt @@ -15,7 +15,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.extensions.compose.subscribeAsState @Composable internal fun ArticleDetailsContent(component: ArticleDetailsComponent, modifier: Modifier = Modifier) { diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/list/ArticleListContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/list/ArticleListContent.kt index f6fd8a36f..b1f369ef6 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/list/ArticleListContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/list/ArticleListContent.kt @@ -16,7 +16,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.sample.shared.multipane.list.ArticleListComponent.Model @Composable diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt index 50e0e47fd..2cdd041b3 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt @@ -24,15 +24,15 @@ import androidx.compose.material.icons.filled.Refresh import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.Children -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.Direction -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.StackAnimation -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.StackAnimator -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.fade -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.isEnter -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.slide -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimation -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.stack.animation.Direction +import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimator +import com.arkivanov.decompose.extensions.compose.stack.animation.fade +import com.arkivanov.decompose.extensions.compose.stack.animation.isEnter +import com.arkivanov.decompose.extensions.compose.stack.animation.slide +import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.sample.shared.SwipeUp import com.arkivanov.sample.shared.cards.CardsContent import com.arkivanov.sample.shared.counters.CountersContent diff --git a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt index d5e9e62a3..4a1f3deff 100644 --- a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt +++ b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt @@ -6,8 +6,8 @@ import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.ui.Modifier import androidx.compose.ui.window.ComposeUIViewController import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.decompose.extensions.compose.jetbrains.PredictiveBackGestureIcon -import com.arkivanov.decompose.extensions.compose.jetbrains.PredictiveBackGestureOverlay +import com.arkivanov.decompose.extensions.compose.PredictiveBackGestureIcon +import com.arkivanov.decompose.extensions.compose.PredictiveBackGestureOverlay import com.arkivanov.essenty.backhandler.BackDispatcher import platform.UIKit.UIViewController diff --git a/settings.gradle.kts b/settings.gradle.kts index f2b615d3b..5dd9d18d0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,7 +27,7 @@ pluginManagement { if (!startParameter.projectProperties.containsKey("check_publication")) { include(":decompose") - include(":extensions-compose-jetbrains") + include(":extensions-compose") include(":extensions-android") include(":sample:shared:shared") include(":sample:shared:compose") From 62db66c8f752cba9960100c1e70e04936b1f0220 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 7 Dec 2023 09:06:49 +0000 Subject: [PATCH 32/89] Added API to discard saved state on Android --- decompose/api/android/decompose.api | 6 +- decompose/build.gradle.kts | 4 + .../DefaultComponentContextBuilder.kt | 57 +++++- .../DefaultComponentContextBuilderTest.kt | 192 ++++++++++++++++++ deps.versions.toml | 5 +- 5 files changed, 251 insertions(+), 13 deletions(-) create mode 100644 decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilderTest.kt diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 527c95c24..103fee90b 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -54,8 +54,10 @@ public final class com/arkivanov/decompose/DefaultComponentContext : com/arkivan public final class com/arkivanov/decompose/DefaultComponentContextBuilderKt { public static final fun DefaultComponentContext (Landroidx/lifecycle/Lifecycle;Landroidx/savedstate/SavedStateRegistry;Landroidx/lifecycle/ViewModelStore;Landroidx/activity/OnBackPressedDispatcher;)Lcom/arkivanov/decompose/DefaultComponentContext; public static synthetic fun DefaultComponentContext$default (Landroidx/lifecycle/Lifecycle;Landroidx/savedstate/SavedStateRegistry;Landroidx/lifecycle/ViewModelStore;Landroidx/activity/OnBackPressedDispatcher;ILjava/lang/Object;)Lcom/arkivanov/decompose/DefaultComponentContext; - public static final fun defaultComponentContext (Landroidx/fragment/app/Fragment;Landroidx/activity/OnBackPressedDispatcher;)Lcom/arkivanov/decompose/DefaultComponentContext; - public static final fun defaultComponentContext (Landroidx/savedstate/SavedStateRegistryOwner;)Lcom/arkivanov/decompose/DefaultComponentContext; + public static final fun defaultComponentContext (Landroidx/fragment/app/Fragment;Landroidx/activity/OnBackPressedDispatcher;ZLkotlin/jvm/functions/Function0;)Lcom/arkivanov/decompose/DefaultComponentContext; + public static final fun defaultComponentContext (Landroidx/savedstate/SavedStateRegistryOwner;ZLkotlin/jvm/functions/Function0;)Lcom/arkivanov/decompose/DefaultComponentContext; + public static synthetic fun defaultComponentContext$default (Landroidx/fragment/app/Fragment;Landroidx/activity/OnBackPressedDispatcher;ZLkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/arkivanov/decompose/DefaultComponentContext; + public static synthetic fun defaultComponentContext$default (Landroidx/savedstate/SavedStateRegistryOwner;ZLkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/arkivanov/decompose/DefaultComponentContext; } public abstract interface annotation class com/arkivanov/decompose/ExperimentalDecomposeApi : java/lang/annotation/Annotation { diff --git a/decompose/build.gradle.kts b/decompose/build.gradle.kts index bd77132b5..84149d133 100644 --- a/decompose/build.gradle.kts +++ b/decompose/build.gradle.kts @@ -58,5 +58,9 @@ kotlin { implementation(deps.androidx.fragment.fragmentKtx) implementation(deps.androidx.lifecycle.lifecycleCommonJava8) } + + android.test.dependencies { + implementation(deps.robolectric.robolectric) + } } } diff --git a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilder.kt b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilder.kt index 6324cee9b..5ef269e14 100644 --- a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilder.kt +++ b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilder.kt @@ -10,8 +10,11 @@ import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryOwner import com.arkivanov.essenty.backhandler.BackHandler import com.arkivanov.essenty.instancekeeper.InstanceKeeper +import com.arkivanov.essenty.instancekeeper.instanceKeeper import com.arkivanov.essenty.lifecycle.asEssentyLifecycle import com.arkivanov.essenty.statekeeper.StateKeeper +import com.arkivanov.essenty.statekeeper.stateKeeper +import kotlinx.serialization.builtins.serializer import androidx.lifecycle.Lifecycle as AndroidLifecycle fun DefaultComponentContext( @@ -30,14 +33,22 @@ fun DefaultComponentContext( /** * Creates a default implementation of [ComponentContext] and attaches it * to the receiver (e.g. an [Activity][android.app.Activity]). + * + * @param discardSavedState a flag indicating whether any previously saved state should be discarded or not, + * default value is `false`. Can be useful for handling deep links in `onCreate`, so that the navigation state + * is not restored and initial state from the deep link is applied instead. + * @param isStateSavingAllowed called before saving the state. When `true` then the state will be saved, + * otherwise it won't. Default value is `true`. */ -fun T.defaultComponentContext(): DefaultComponentContext where +fun T.defaultComponentContext( + discardSavedState: Boolean = false, + isStateSavingAllowed: () -> Boolean = { true }, +): DefaultComponentContext where T : SavedStateRegistryOwner, T : OnBackPressedDispatcherOwner, T : ViewModelStoreOwner, T : LifecycleOwner = - DefaultComponentContext( - lifecycle = (this as LifecycleOwner).lifecycle, - savedStateRegistry = savedStateRegistry, - viewModelStore = viewModelStore, - onBackPressedDispatcher = onBackPressedDispatcher, + defaultComponentContext( + backHandler = BackHandler(onBackPressedDispatcher), + discardSavedState = discardSavedState, + isStateSavingAllowed = isStateSavingAllowed, ) /** @@ -45,18 +56,44 @@ fun T.defaultComponentContext(): DefaultComponentContext where * * @param onBackPressedDispatcher an optional [OnBackPressedDispatcher] for back button handling, * or `null` if back button handling is not required. Can be obtained via `requireActivity().onBackPressedDispatcher`. + * @param discardSavedState a flag indicating whether any previously saved state should be discarded or not, + * default value is `false`. Can be useful for handling deep links in `onCreate`, so that the navigation state + * is not restored and initial state from the deep link is applied instead. + * @param isStateSavingAllowed called before saving the state. When `true` then the state will be saved, + * otherwise it won't. Default value is `true`. */ fun Fragment.defaultComponentContext( onBackPressedDispatcher: OnBackPressedDispatcher?, + discardSavedState: Boolean = false, + isStateSavingAllowed: () -> Boolean = { true }, ): DefaultComponentContext = - DefaultComponentContext( - lifecycle = lifecycle.asEssentyLifecycle(), - stateKeeper = StateKeeper(savedStateRegistry), - instanceKeeper = InstanceKeeper(viewModelStore), + defaultComponentContext( backHandler = onBackPressedDispatcher?.let { BackHandler( onBackPressedDispatcher = it, lifecycleOwner = this, ) }, + discardSavedState = discardSavedState, + isStateSavingAllowed = isStateSavingAllowed, ) + +private fun T.defaultComponentContext( + backHandler: BackHandler?, + discardSavedState: Boolean, + isStateSavingAllowed: () -> Boolean, +): DefaultComponentContext where + T : SavedStateRegistryOwner, T : ViewModelStoreOwner, T : LifecycleOwner { + val stateKeeper = stateKeeper(discardSavedState = discardSavedState, isSavingAllowed = isStateSavingAllowed) + val marker = stateKeeper.consume(key = KEY_STATE_MARKER, strategy = String.serializer()) + stateKeeper.register(key = KEY_STATE_MARKER, strategy = String.serializer()) { "marker" } + + return DefaultComponentContext( + lifecycle = lifecycle.asEssentyLifecycle(), + stateKeeper = stateKeeper, + instanceKeeper = instanceKeeper(discardRetainedInstances = marker == null), + backHandler = backHandler, + ) +} + +private const val KEY_STATE_MARKER = "DefaultComponentContext_state_marker" diff --git a/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilderTest.kt b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilderTest.kt new file mode 100644 index 000000000..ae33f473c --- /dev/null +++ b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilderTest.kt @@ -0,0 +1,192 @@ +package com.arkivanov.decompose + +import android.os.Bundle +import android.os.Parcel +import androidx.activity.OnBackPressedDispatcher +import androidx.activity.OnBackPressedDispatcherOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import androidx.lifecycle.ViewModelStore +import androidx.lifecycle.ViewModelStoreOwner +import androidx.savedstate.SavedStateRegistry +import androidx.savedstate.SavedStateRegistryController +import androidx.savedstate.SavedStateRegistryOwner +import com.arkivanov.decompose.router.TestInstance +import com.arkivanov.essenty.backhandler.BackCallback +import com.arkivanov.essenty.instancekeeper.getOrCreate +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotSame +import kotlin.test.assertNull +import kotlin.test.assertSame +import kotlin.test.assertTrue + +@Suppress("TestFunctionName") +@RunWith(RobolectricTestRunner::class) +class DefaultComponentContextBuilderTest { + + @Test + fun saves_and_restores_state() { + var owner = TestOwner() + var ctx = owner.defaultComponentContext() + ctx.stateKeeper.register(key = "key") { "saved_state" } + + owner = owner.recreate() + ctx = owner.defaultComponentContext() + val restoredState = ctx.stateKeeper.consume(key = "key") + + assertEquals("saved_state", restoredState) + } + + @Test + fun retains_instances() { + var owner = TestOwner() + var ctx = owner.defaultComponentContext() + val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + owner = owner.recreate() + ctx = owner.defaultComponentContext() + val instance2 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + assertSame(instance1, instance2) + } + + @Test + fun GIVEN_isStateSavingAllowed_is_false_on_save_THEN_state_not_saved() { + var owner = TestOwner() + var ctx = owner.defaultComponentContext(isStateSavingAllowed = { false }) + ctx.stateKeeper.register(key = "key") { "saved_state" } + + owner = owner.recreate() + ctx = owner.defaultComponentContext() + val restoredState = ctx.stateKeeper.consume(key = "key") + + assertNull(restoredState) + } + + @Test + fun GIVEN_isStateSavingAllowed_is_false_on_save_THEN_instances_not_retained() { + var owner = TestOwner() + var ctx = owner.defaultComponentContext(isStateSavingAllowed = { false }) + val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + owner = owner.recreate() + ctx = owner.defaultComponentContext() + val instance2 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + assertNotSame(instance1, instance2) + } + + @Test + fun GIVEN_isStateSavingAllowed_is_false_on_save_THEN_old_instances_destroyed() { + var owner = TestOwner() + var ctx = owner.defaultComponentContext(isStateSavingAllowed = { false }) + val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + owner = owner.recreate() + owner.defaultComponentContext() + + assertTrue(instance1.isDestroyed) + } + + @Test + fun GIVEN_discardSavedState_is_true_on_restore_THEN_discards_saved_state() { + var owner = TestOwner() + var ctx = owner.defaultComponentContext() + ctx.stateKeeper.register(key = "key") { "saved_state" } + + owner = owner.recreate() + ctx = owner.defaultComponentContext(discardSavedState = true) + val restoredState = ctx.stateKeeper.consume(key = "key") + + assertNull(restoredState) + } + + @Test + fun GIVEN_discardSavedState_is_true_on_restore_THEN_instances_not_retained() { + var owner = TestOwner() + var ctx = owner.defaultComponentContext() + val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + owner = owner.recreate() + ctx = owner.defaultComponentContext(discardSavedState = true) + val instance2 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + assertNotSame(instance1, instance2) + } + + @Test + fun GIVEN_discardSavedState_is_true_on_restore_THEN_old_instances_destroyed() { + var owner = TestOwner() + val ctx = owner.defaultComponentContext() + val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + owner = owner.recreate() + owner.defaultComponentContext(discardSavedState = true) + + assertTrue(instance1.isDestroyed) + } + + @Test + fun GIVEN_enabled_BackCallback_registered_WHEN_onBackPressed_THEN_callback_called() { + val owner = TestOwner() + val ctx = owner.defaultComponentContext() + var isCalled = false + ctx.backHandler.register(BackCallback { isCalled = true }) + + owner.onBackPressedDispatcher.onBackPressed() + + assertTrue(isCalled) + } + + @Test + fun GIVEN_disabled_BackCallback_registered_WHEN_onBackPressed_THEN_callback_not_called() { + val owner = TestOwner() + val ctx = owner.defaultComponentContext() + var isCalled = false + ctx.backHandler.register(BackCallback(isEnabled = false) { isCalled = true }) + + owner.onBackPressedDispatcher.onBackPressed() + + assertFalse(isCalled) + } + + private class TestOwner( + savedState: Bundle = Bundle(), + override val viewModelStore: ViewModelStore = ViewModelStore(), + ) : LifecycleOwner, SavedStateRegistryOwner, ViewModelStoreOwner, OnBackPressedDispatcherOwner { + private val savedStateRegistryController: SavedStateRegistryController = SavedStateRegistryController.create(this) + override val lifecycle: Lifecycle = LifecycleRegistry(this) + override val savedStateRegistry: SavedStateRegistry get() = savedStateRegistryController.savedStateRegistry + override val onBackPressedDispatcher: OnBackPressedDispatcher = OnBackPressedDispatcher() + + init { + savedStateRegistryController.performRestore(savedState) + } + + fun recreate(): TestOwner { + val bundle = Bundle() + savedStateRegistryController.performSave(bundle) + + return TestOwner(savedState = bundle.parcelize().deparcelize(), viewModelStore = viewModelStore) + } + + private fun Bundle.parcelize(): ByteArray { + val parcel = Parcel.obtain() + parcel.writeBundle(this) + return parcel.marshall() + } + + private fun ByteArray.deparcelize(): Bundle { + val parcel = Parcel.obtain() + parcel.unmarshall(this, 0, size) + parcel.setDataPosition(0) + + return requireNotNull(parcel.readBundle()) + } + } +} diff --git a/deps.versions.toml b/deps.versions.toml index ce600e9a3..28f4eb537 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -2,7 +2,7 @@ decompose = "2.2.0-compose-experimental" kotlin = "1.9.20" -essenty = "2.0.0-dev01" +essenty = "2.0.0-dev02" reaktive = "1.2.3" junit = "4.13.2" jetbrainsCompose = "1.5.10" @@ -12,6 +12,7 @@ jetbrainsKotlinxSerialization = "1.6.0" jetbrainsBinaryCompatibilityValidator = "0.13.2" jetpackCompose = "1.5.0" jetpackComposeCompiler = "1.5.4" +robolectric = "4.9.1" androidGradle = "8.0.2" androidMaterial = "1.6.1" androidPlay = "1.10.3" @@ -48,6 +49,8 @@ jetbrains-kotlin-serializationGradlePlug = { group = "org.jetbrains.kotlin", nam jetbrains-kotlinx-kotlinxSerializationCore = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "jetbrainsKotlinxSerialization" } jetbrains-kotlinx-kotlinxSerializationJson = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "jetbrainsKotlinxSerialization" } +robolectric-robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } + androidx-compose-runtime-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "jetpackCompose" } android-gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradle" } From c8279f9e91d06584925713f4410937360346b1ba Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 7 Dec 2023 14:50:02 +0000 Subject: [PATCH 33/89] Simplified type parameters of the children function --- .../arkivanov/decompose/router/children/ChildrenFactory.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt index 8d697c759..f37345e69 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt @@ -18,7 +18,7 @@ import kotlinx.serialization.Serializable * so it's automatically saved and restored. This method can be used if the custom save/restore logic * is not required. */ -fun ComponentContext.children( +fun , S : Any> ComponentContext.children( source: NavigationSource, stateSerializer: KSerializer?, initialState: () -> N, @@ -29,7 +29,7 @@ fun ComponentContext.children( onEventComplete: (event: E, newState: N, oldState: N) -> Unit = { _, _, _ -> }, backTransformer: (state: N) -> (() -> N)? = { null }, childFactory: (configuration: C, componentContext: ComponentContext) -> T, -): Value where N : NavState, N : Any = +): Value = children( source = source, saveState = { state -> From 759b43293e74bb9b1c32da02dc76135a9be3d86d Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 8 Dec 2023 14:46:34 +0000 Subject: [PATCH 34/89] Updated Multiplatform Compose to 1.6.0-alpha01 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 87092907b..75fc8ac80 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -5,7 +5,7 @@ kotlin = "1.9.21" essenty = "2.0.0-dev02" reaktive = "1.2.3" junit = "4.13.2" -jetbrainsCompose = "1.5.11" +jetbrainsCompose = "1.6.0-alpha01" jetbrainsKotlinWrappers = "1.0.0-pre.608" jetbrainsKotlinxCoroutines = "1.6.4" jetbrainsKotlinxSerialization = "1.6.0" From cc866a509f66a848e6f98eabed642b8fe9c50e42 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 8 Dec 2023 19:36:49 +0000 Subject: [PATCH 35/89] Added support for WASM for Browser --- build.gradle.kts | 1 + decompose/build.gradle.kts | 19 +++++- .../decompose/value/MutableValueTest.kt | 68 +++++++++---------- .../com/arkivanov/decompose/Utils.nonJs.kt | 0 .../decompose/AbstractThreadingTest.kt | 3 +- .../arkivanov/decompose/RelayThreadingTest.kt | 0 .../AbstractMutableValueThreadingTest.kt | 0 .../operator/AbstractValueMapThreadingTest.kt | 0 .../kotlin/com/arkivanov/decompose/Utils.kt | 6 ++ .../kotlin/com/arkivanov/decompose/Lock.kt | 0 .../decompose/errorhandler/PrintError.kt | 0 .../decompose/mainthread/CheckMainThread.kt | 0 deps.versions.toml | 6 +- extensions-compose/build.gradle.kts | 1 + gradle.properties | 1 + 15 files changed, 65 insertions(+), 40 deletions(-) rename decompose/src/{nonJsMain => nonWebMain}/kotlin/com/arkivanov/decompose/Utils.nonJs.kt (100%) rename decompose/src/{nonJsTest => nonWebTest}/kotlin/com/arkivanov/decompose/AbstractThreadingTest.kt (93%) rename decompose/src/{nonJsTest => nonWebTest}/kotlin/com/arkivanov/decompose/RelayThreadingTest.kt (100%) rename decompose/src/{nonJsTest => nonWebTest}/kotlin/com/arkivanov/decompose/value/AbstractMutableValueThreadingTest.kt (100%) rename decompose/src/{nonJsTest => nonWebTest}/kotlin/com/arkivanov/decompose/value/operator/AbstractValueMapThreadingTest.kt (100%) create mode 100644 decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/Utils.kt rename decompose/src/{jsMain => webMain}/kotlin/com/arkivanov/decompose/Lock.kt (100%) rename decompose/src/{jsMain => webMain}/kotlin/com/arkivanov/decompose/errorhandler/PrintError.kt (100%) rename decompose/src/{jsMain => webMain}/kotlin/com/arkivanov/decompose/mainthread/CheckMainThread.kt (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 3b8820646..ccd1247e7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,6 +33,7 @@ setupDefaults( androidTarget() jvm() js { browser() } + wasmJs { browser() } iosCompat() watchosCompat() tvosCompat() diff --git a/decompose/build.gradle.kts b/decompose/build.gradle.kts index 84149d133..b7ac5927d 100644 --- a/decompose/build.gradle.kts +++ b/decompose/build.gradle.kts @@ -5,6 +5,7 @@ import com.arkivanov.gradle.setupBinaryCompatibilityValidator import com.arkivanov.gradle.setupMultiplatform import com.arkivanov.gradle.setupPublication import com.arkivanov.gradle.setupSourceSets +import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask plugins { id("kotlin-multiplatform") @@ -27,10 +28,15 @@ kotlin { val darwin by bundle() val itvos by bundle() val js by bundle() - val nonJs by bundle() + val wasmJs by bundle() + val nonWeb by bundle() + val web by bundle() (darwin) dependsOn common - (allSet - js) dependsOn nonJs + nonWeb dependsOn common + (allSet - js - wasmJs) dependsOn nonWeb + web dependsOn common + (js + wasmJs) dependsOn web (iosSet + tvosSet) dependsOn itvos (darwinSet - iosSet - tvosSet + itvos) dependsOn darwin @@ -51,6 +57,9 @@ kotlin { common.test.dependencies { implementation(deps.jetbrains.kotlinx.kotlinxCoroutinesCore) + + // Workaround: https://github.com/Kotlin/kotlinx.coroutines/issues/3968 + implementation("org.jetbrains.kotlinx:atomicfu:0.23.1") } android.main.dependencies { @@ -64,3 +73,9 @@ kotlin { } } } + +tasks.named>("compileKotlinWasmJs").configure { + compilerOptions { + freeCompilerArgs.add("-Xwasm-kclass-fqn") + } +} diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/MutableValueTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/MutableValueTest.kt index 41d54c11e..9c7f3fb2e 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/MutableValueTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/value/MutableValueTest.kt @@ -9,142 +9,142 @@ import kotlin.test.assertTrue @Suppress("TestFunctionName") class MutableValueTest { - private val value = MutableValue(0) + private val value = MutableValue("0") @Test fun WHEN_created_THEN_initial_value() { - assertEquals(0, value.value) + assertEquals("0", value.value) } @Test fun WHEN_value_changed_THEN_new_value() { - value.value = 1 + value.value = "1" - assertEquals(1, value.value) + assertEquals("1", value.value) } @Test fun WHEN_subscribe_THEN_current_value_emitted() { - val values = ArrayList() + val values = ArrayList() value.subscribe { values += it } - assertContentEquals(listOf(0), values) + assertContentEquals(listOf("0"), values) } @Test fun GIVEN_multiple_observers_subscribed_WHEN_value_changed_THEN_new_value_emitted() { - val values = List(10) { ArrayList() } + val values = List(10) { ArrayList() } repeat(10) { index -> value.subscribe { values[index] += it } } - value.value = 1 - value.value = 2 - value.value = 3 + value.value = "1" + value.value = "2" + value.value = "3" - assertContentEquals(List(10) { listOf(0, 1, 2, 3) }, values) + assertContentEquals(List(10) { listOf("0", "1", "2", "3") }, values) } @Test fun GIVEN_unsubscribed_WHEN_value_changed_THEN_not_emitted() { - val values = ArrayList() + val values = ArrayList() val cancellation = value.subscribe { values += it } cancellation.cancel() values.clear() - value.value = 1 + value.value = "1" assertContentEquals(emptyList(), values) } @Test fun GIVEN_multiple_subscribes_and_one_unsubscribed_WHEN_value_changed_THEN_value_emitted_to_subscribed() { - val values = ArrayList() + val values = ArrayList() val cancellation = value.subscribe {} value.subscribe { values += it } cancellation.cancel() values.clear() - value.value = 1 + value.value = "1" - assertContentEquals(listOf(1), values) + assertContentEquals(listOf("1"), values) } @Test fun GIVEN_multiple_subscribes_and_one_unsubscribed_WHEN_value_changed_THEN_value_not_emitted_to_unsubscribed() { - val values = ArrayList() + val values = ArrayList() val cancellation = value.subscribe { values += it } value.subscribe {} cancellation.cancel() values.clear() - value.value = 1 + value.value = "1" assertContentEquals(emptyList(), values) } @Test fun WHEN_compareAndSet_with_expected_value_THEN_returns_true() { - val result = value.compareAndSet(expected = 0, new = 1) + val result = value.compareAndSet(expected = value.value, new = "1") assertTrue(result) } @Test fun WHEN_compareAndSet_with_expected_value_THEN_new_value() { - value.compareAndSet(expected = 0, new = 1) + value.compareAndSet(expected = value.value, new = "1") - assertEquals(1, value.value) + assertEquals("1", value.value) } @Test fun WHEN_compareAndSet_with_unexpected_value_THEN_returns_false() { - val result = value.compareAndSet(expected = 1, new = 2) + val result = value.compareAndSet(expected = "1", new = "2") assertFalse(result) } @Test fun WHEN_compareAndSet_with_unexpected_value_THEN_old_value() { - value.compareAndSet(expected = 1, new = 2) + value.compareAndSet(expected = "1", new = "2") - assertEquals(0, value.value) + assertEquals("0", value.value) } @Test fun WHEN_update_THEN_new_value() { - value.update { it + 1 } + value.update { it + "1" } - assertEquals(1, value.value) + assertEquals("01", value.value) } @Test fun WHEN_getAndUpdate_THEN_new_value() { - value.getAndUpdate { it + 1 } + value.getAndUpdate { it + "1" } - assertEquals(1, value.value) + assertEquals("01", value.value) } @Test fun WHEN_getAndUpdate_THEN_returns_old_value() { - val result = value.getAndUpdate { it + 1 } + val result = value.getAndUpdate { it + "1" } - assertEquals(0, result) + assertEquals("0", result) } @Test fun WHEN_updateAndGet_THEN_new_value() { - value.updateAndGet { it + 1 } + value.updateAndGet { it + "1" } - assertEquals(1, value.value) + assertEquals("01", value.value) } @Test fun WHEN_updateAndGet_THEN_returns_new_value() { - val result = value.updateAndGet { it + 1 } + val result = value.updateAndGet { it + "1" } - assertEquals(1, result) + assertEquals("01", result) } } diff --git a/decompose/src/nonJsMain/kotlin/com/arkivanov/decompose/Utils.nonJs.kt b/decompose/src/nonWebMain/kotlin/com/arkivanov/decompose/Utils.nonJs.kt similarity index 100% rename from decompose/src/nonJsMain/kotlin/com/arkivanov/decompose/Utils.nonJs.kt rename to decompose/src/nonWebMain/kotlin/com/arkivanov/decompose/Utils.nonJs.kt diff --git a/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/AbstractThreadingTest.kt b/decompose/src/nonWebTest/kotlin/com/arkivanov/decompose/AbstractThreadingTest.kt similarity index 93% rename from decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/AbstractThreadingTest.kt rename to decompose/src/nonWebTest/kotlin/com/arkivanov/decompose/AbstractThreadingTest.kt index a2b979272..35c5e8d65 100644 --- a/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/AbstractThreadingTest.kt +++ b/decompose/src/nonWebTest/kotlin/com/arkivanov/decompose/AbstractThreadingTest.kt @@ -1,6 +1,7 @@ package com.arkivanov.decompose import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update @@ -13,11 +14,11 @@ import kotlin.test.AfterTest import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds +@OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class) abstract class AbstractThreadingTest { protected open val threadCount: Int = 8 - @OptIn(DelicateCoroutinesApi::class) private val dispatcher by lazy { newFixedThreadPoolContext(nThreads = threadCount, name = "AbstractThreadingTest pool") } @AfterTest diff --git a/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/RelayThreadingTest.kt b/decompose/src/nonWebTest/kotlin/com/arkivanov/decompose/RelayThreadingTest.kt similarity index 100% rename from decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/RelayThreadingTest.kt rename to decompose/src/nonWebTest/kotlin/com/arkivanov/decompose/RelayThreadingTest.kt diff --git a/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/AbstractMutableValueThreadingTest.kt b/decompose/src/nonWebTest/kotlin/com/arkivanov/decompose/value/AbstractMutableValueThreadingTest.kt similarity index 100% rename from decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/AbstractMutableValueThreadingTest.kt rename to decompose/src/nonWebTest/kotlin/com/arkivanov/decompose/value/AbstractMutableValueThreadingTest.kt diff --git a/decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/operator/AbstractValueMapThreadingTest.kt b/decompose/src/nonWebTest/kotlin/com/arkivanov/decompose/value/operator/AbstractValueMapThreadingTest.kt similarity index 100% rename from decompose/src/nonJsTest/kotlin/com/arkivanov/decompose/value/operator/AbstractValueMapThreadingTest.kt rename to decompose/src/nonWebTest/kotlin/com/arkivanov/decompose/value/operator/AbstractValueMapThreadingTest.kt diff --git a/decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/Utils.kt b/decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/Utils.kt new file mode 100644 index 000000000..ea6142a46 --- /dev/null +++ b/decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/Utils.kt @@ -0,0 +1,6 @@ +package com.arkivanov.decompose + +import kotlin.reflect.KClass + +internal actual val KClass<*>.uniqueName: String? + get() = qualifiedName diff --git a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/Lock.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/Lock.kt similarity index 100% rename from decompose/src/jsMain/kotlin/com/arkivanov/decompose/Lock.kt rename to decompose/src/webMain/kotlin/com/arkivanov/decompose/Lock.kt diff --git a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/errorhandler/PrintError.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/errorhandler/PrintError.kt similarity index 100% rename from decompose/src/jsMain/kotlin/com/arkivanov/decompose/errorhandler/PrintError.kt rename to decompose/src/webMain/kotlin/com/arkivanov/decompose/errorhandler/PrintError.kt diff --git a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/mainthread/CheckMainThread.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/mainthread/CheckMainThread.kt similarity index 100% rename from decompose/src/jsMain/kotlin/com/arkivanov/decompose/mainthread/CheckMainThread.kt rename to decompose/src/webMain/kotlin/com/arkivanov/decompose/mainthread/CheckMainThread.kt diff --git a/deps.versions.toml b/deps.versions.toml index 75fc8ac80..35616f58b 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -2,13 +2,13 @@ decompose = "2.2.1-compose-experimental" kotlin = "1.9.21" -essenty = "2.0.0-dev02" +essenty = "2.0.0-dev03" reaktive = "1.2.3" junit = "4.13.2" jetbrainsCompose = "1.6.0-alpha01" jetbrainsKotlinWrappers = "1.0.0-pre.608" -jetbrainsKotlinxCoroutines = "1.6.4" -jetbrainsKotlinxSerialization = "1.6.0" +jetbrainsKotlinxCoroutines = "1.8.0-RC" +jetbrainsKotlinxSerialization = "1.6.2" jetbrainsBinaryCompatibilityValidator = "0.13.2" jetpackCompose = "1.5.0" jetpackComposeCompiler = "1.5.6" diff --git a/extensions-compose/build.gradle.kts b/extensions-compose/build.gradle.kts index 0501e8134..6fd8b895c 100644 --- a/extensions-compose/build.gradle.kts +++ b/extensions-compose/build.gradle.kts @@ -20,6 +20,7 @@ setupMultiplatform { macosCompat() iosCompat() js { browser() } + wasmJs { browser() } } setupPublication() diff --git a/gradle.properties b/gradle.properties index cd97a4bb4..49006c23e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,6 +10,7 @@ kotlin.mpp.androidSourceSetLayoutVersion=2 kotlin.mpp.applyDefaultHierarchyTemplate=false org.jetbrains.compose.experimental.macos.enabled=true org.jetbrains.compose.experimental.jscanvas.enabled=true +org.jetbrains.compose.experimental.wasm.enabled=true # For compatibility with Kotlin 1.9.0 android.experimental.lint.version=8.1.0 From 5fc5825cabf26e1456f240fc790226c6dc5373ef Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 8 Dec 2023 22:06:58 +0000 Subject: [PATCH 36/89] Clean-up samples --- .../UserInterfaceState.xcuserstate | Bin 27541 -> 27941 bytes .../shared/utils/AlertDialog.android.kt | 23 ------ .../sample/shared/counters/CountersContent.kt | 3 +- .../sample/shared/dialog/DialogContent.kt | 2 +- .../sample/shared/root/RootContent.kt | 48 ----------- .../sample/shared/utils/AlertDialog.kt | 13 --- .../sample/shared/utils/AlertDialog.ios.kt | 78 ------------------ .../sample/shared/utils/AlertDialog.ios.kt | 78 ------------------ .../sample/shared/utils/AlertDialog.jvm.kt | 23 ------ 9 files changed, 2 insertions(+), 266 deletions(-) delete mode 100644 sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.android.kt delete mode 100644 sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.kt delete mode 100644 sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt delete mode 100644 sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt delete mode 100644 sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.jvm.kt diff --git a/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate index 95ab41aad3b2209ae50397fc46601da7abbdcc82..b88e9ab7ab19588e2a9ebc984d9bedffbbf3d049 100644 GIT binary patch delta 14629 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 diff --git a/sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.android.kt b/sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.android.kt deleted file mode 100644 index 58afbb7b8..000000000 --- a/sample/shared/compose/src/androidMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.android.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.arkivanov.sample.shared.utils - -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier - -@OptIn(ExperimentalMaterialApi::class) -@Composable -internal actual fun AlertDialog( - onDismissRequest: () -> Unit, - confirmButton: @Composable () -> Unit, - modifier: Modifier, - title: @Composable () -> Unit, - text: @Composable () -> Unit, -) { - androidx.compose.material.AlertDialog( - onDismissRequest = onDismissRequest, - confirmButton = confirmButton, - modifier = modifier, - title = title, - text = text, - ) -} diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt index 2574f68f3..9827b77c0 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/CountersContent.kt @@ -9,7 +9,6 @@ import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.decompose.FaultyDecomposeApi import com.arkivanov.decompose.extensions.compose.stack.Children import com.arkivanov.decompose.extensions.compose.stack.animation.fade import com.arkivanov.decompose.extensions.compose.stack.animation.plus @@ -18,7 +17,7 @@ import com.arkivanov.decompose.extensions.compose.stack.animation.scale import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.sample.shared.counters.counter.CounterContent -@OptIn(ExperimentalDecomposeApi::class, FaultyDecomposeApi::class) +@OptIn(ExperimentalDecomposeApi::class) @Composable internal fun CountersContent(component: CountersComponent, modifier: Modifier = Modifier) { Children( diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dialog/DialogContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dialog/DialogContent.kt index 31df7d6bc..0bcb73b35 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dialog/DialogContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/dialog/DialogContent.kt @@ -1,12 +1,12 @@ package com.arkivanov.sample.shared.dialog import androidx.compose.foundation.layout.widthIn +import androidx.compose.material.AlertDialog import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.arkivanov.sample.shared.utils.AlertDialog @Composable fun DialogContent(dialogComponent: DialogComponent) { diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt index 2cdd041b3..f4dd8bfec 100644 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt +++ b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/root/RootContent.kt @@ -25,12 +25,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.stack.Children -import com.arkivanov.decompose.extensions.compose.stack.animation.Direction -import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation -import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimator import com.arkivanov.decompose.extensions.compose.stack.animation.fade -import com.arkivanov.decompose.extensions.compose.stack.animation.isEnter -import com.arkivanov.decompose.extensions.compose.stack.animation.slide import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.sample.shared.SwipeUp @@ -68,10 +63,7 @@ private fun Children(component: RootComponent, modifier: Modifier = Modifier) { Children( stack = component.childStack, modifier = modifier, - - // Workaround for https://issuetracker.google.com/issues/270656235 animation = stackAnimation(fade()), -// animation = tabAnimation(), ) { when (val child = it.instance) { is CountersChild -> CountersContent(component = child.component, modifier = Modifier.fillMaxSize()) @@ -146,15 +138,6 @@ private fun BottomBar(component: RootComponent, modifier: Modifier = Modifier) { } } -@Composable -private fun tabAnimation(): StackAnimation = - stackAnimation { child, otherChild, direction -> - val index = child.instance.index - val otherIndex = otherChild.instance.index - val anim = slide() - if ((index > otherIndex) == direction.isEnter) anim else anim.flipSide() - } - private val Child.index: Int get() = when (this) { @@ -165,37 +148,6 @@ private val Child.index: Int is CustomNavigationChild -> 4 } -private fun StackAnimator.flipSide(): StackAnimator = - FlipSideStackAnimator(animator = this) - -/* - * Can't be anonymous. See: - * https://github.com/JetBrains/compose-jb/issues/2688 - * https://github.com/JetBrains/compose-jb/issues/2612 - */ -private class FlipSideStackAnimator( - private val animator: StackAnimator, -) : StackAnimator { - - @Composable - override fun invoke(direction: Direction, isInitial: Boolean, onFinished: () -> Unit, content: @Composable (Modifier) -> Unit) { - animator( - direction = direction.flipSide(), - isInitial = isInitial, - onFinished = onFinished, - content = content, - ) - } -} - -private fun Direction.flipSide(): Direction = - when (this) { - Direction.ENTER_FRONT -> Direction.ENTER_BACK - Direction.EXIT_FRONT -> Direction.EXIT_BACK - Direction.ENTER_BACK -> Direction.ENTER_FRONT - Direction.EXIT_BACK -> Direction.EXIT_FRONT - } - @Preview @Composable internal fun RootContentPreview() { diff --git a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.kt b/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.kt deleted file mode 100644 index 846ca5b50..000000000 --- a/sample/shared/compose/src/commonMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.arkivanov.sample.shared.utils - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier - -@Composable -internal expect fun AlertDialog( - onDismissRequest: () -> Unit, - confirmButton: @Composable () -> Unit, - modifier: Modifier, - title: @Composable () -> Unit, - text: @Composable () -> Unit, -) diff --git a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt deleted file mode 100644 index 7cc024de5..000000000 --- a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.arkivanov.sample.shared.utils - -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material.ContentAlpha -import androidx.compose.material.LocalContentAlpha -import androidx.compose.material.MaterialTheme -import androidx.compose.material.ProvideTextStyle -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntRect -import androidx.compose.ui.unit.IntSize -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Popup -import androidx.compose.ui.window.PopupPositionProvider - -@Composable -internal actual fun AlertDialog( - onDismissRequest: () -> Unit, - confirmButton: @Composable () -> Unit, - modifier: Modifier, - title: @Composable () -> Unit, - text: @Composable () -> Unit, -) { - Popup( - popupPositionProvider = object : PopupPositionProvider { - override fun calculatePosition( - anchorBounds: IntRect, - windowSize: IntSize, - layoutDirection: LayoutDirection, - popupContentSize: IntSize - ): IntOffset = IntOffset.Zero - }, - focusable = true, - onDismissRequest = onDismissRequest, - ) { - Box( - modifier = Modifier - .fillMaxSize() - .pointerInput(onDismissRequest) { - detectTapGestures(onPress = { onDismissRequest() }) - }, - contentAlignment = Alignment.Center - ) { - Surface( - elevation = 24.dp, - modifier = modifier.align(Alignment.Center), - ) { - Column { - Box(modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 24.dp)) { - CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { - ProvideTextStyle(MaterialTheme.typography.subtitle1, title) - } - } - - Box(modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 24.dp)) { - CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { - ProvideTextStyle(MaterialTheme.typography.body2, text) - } - } - - Box(modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp).align(Alignment.End)) { - confirmButton() - } - } - } - } - } -} diff --git a/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt b/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt deleted file mode 100644 index 7cc024de5..000000000 --- a/sample/shared/compose/src/jsMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.ios.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.arkivanov.sample.shared.utils - -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material.ContentAlpha -import androidx.compose.material.LocalContentAlpha -import androidx.compose.material.MaterialTheme -import androidx.compose.material.ProvideTextStyle -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntRect -import androidx.compose.ui.unit.IntSize -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Popup -import androidx.compose.ui.window.PopupPositionProvider - -@Composable -internal actual fun AlertDialog( - onDismissRequest: () -> Unit, - confirmButton: @Composable () -> Unit, - modifier: Modifier, - title: @Composable () -> Unit, - text: @Composable () -> Unit, -) { - Popup( - popupPositionProvider = object : PopupPositionProvider { - override fun calculatePosition( - anchorBounds: IntRect, - windowSize: IntSize, - layoutDirection: LayoutDirection, - popupContentSize: IntSize - ): IntOffset = IntOffset.Zero - }, - focusable = true, - onDismissRequest = onDismissRequest, - ) { - Box( - modifier = Modifier - .fillMaxSize() - .pointerInput(onDismissRequest) { - detectTapGestures(onPress = { onDismissRequest() }) - }, - contentAlignment = Alignment.Center - ) { - Surface( - elevation = 24.dp, - modifier = modifier.align(Alignment.Center), - ) { - Column { - Box(modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 24.dp)) { - CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { - ProvideTextStyle(MaterialTheme.typography.subtitle1, title) - } - } - - Box(modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 24.dp)) { - CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { - ProvideTextStyle(MaterialTheme.typography.body2, text) - } - } - - Box(modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp).align(Alignment.End)) { - confirmButton() - } - } - } - } - } -} diff --git a/sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.jvm.kt b/sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.jvm.kt deleted file mode 100644 index 58afbb7b8..000000000 --- a/sample/shared/compose/src/jvmMain/kotlin/com/arkivanov/sample/shared/utils/AlertDialog.jvm.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.arkivanov.sample.shared.utils - -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier - -@OptIn(ExperimentalMaterialApi::class) -@Composable -internal actual fun AlertDialog( - onDismissRequest: () -> Unit, - confirmButton: @Composable () -> Unit, - modifier: Modifier, - title: @Composable () -> Unit, - text: @Composable () -> Unit, -) { - androidx.compose.material.AlertDialog( - onDismissRequest = onDismissRequest, - confirmButton = confirmButton, - modifier = modifier, - title = title, - text = text, - ) -} From d63794073ba6a55378dd1bed5d4073184c72d21c Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 8 Dec 2023 23:02:43 +0000 Subject: [PATCH 37/89] Build app-ios-compose on CI --- .github/workflows/build.yml | 2 ++ .../UserInterfaceState.xcuserstate | Bin 27379 -> 27869 bytes sample/shared/compose/build.gradle.kts | 34 +++++++++--------- sample/shared/shared/build.gradle.kts | 28 ++++++++------- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a4312f084..441b37572 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,3 +43,5 @@ jobs: arguments: build -Dsplit_targets - name: Build iOS sample run: xcodebuild -project sample/app-ios/app-ios.xcodeproj -scheme app-ios -sdk iphonesimulator -arch x86_64 build + - name: Build iOS Compose sample + run: xcodebuild -project sample/app-ios-compose/app-ios-compose.xcodeproj -scheme app-ios-compose -sdk iphonesimulator -arch x86_64 build diff --git a/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate b/sample/app-ios-compose/app-ios-compose.xcodeproj/project.xcworkspace/xcuserdata/arkivanov.xcuserdatad/UserInterfaceState.xcuserstate index 9d0313ba6682a7720b0bfd4bb1bc13452c5fe71f..2e6a2b67cc40410f5d06e079b7fe235b3fdc1764 100644 GIT binary patch delta 15530 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 XsrJKvKwmDk{|Fl#|95=%oALhu8Z3wS diff --git a/sample/shared/compose/build.gradle.kts b/sample/shared/compose/build.gradle.kts index 6ee286fa7..34ff69771 100644 --- a/sample/shared/compose/build.gradle.kts +++ b/sample/shared/compose/build.gradle.kts @@ -25,27 +25,29 @@ android { } kotlin { - targets - .filterIsInstance() - .filter { it.konanTarget.family == Family.IOS } - .forEach { - it.binaries { - framework { - baseName = "Shared" // Used by app-ios-compose - export(project(":decompose")) - export(project(":sample:shared:shared")) + if ("XCODE_VERSION_MAJOR" in System.getenv().keys) { + targets + .filterIsInstance() + .filter { it.konanTarget.family == Family.IOS } + .forEach { + it.binaries { + framework { + baseName = "Shared" // Used by app-ios-compose + export(project(":decompose")) + export(project(":sample:shared:shared")) - // Optional, only if you need Predictive Back Gesture on Darwin (Apple) targets - export(deps.essenty.backHandler) + // Optional, only if you need Predictive Back Gesture on Darwin (Apple) targets + export(deps.essenty.backHandler) - // Optional, only if you need state preservation on Darwin (Apple) targets - export(deps.essenty.stateKeeper) + // Optional, only if you need state preservation on Darwin (Apple) targets + export(deps.essenty.stateKeeper) - // Optional, only if you need state preservation on Darwin (Apple) targets - export(deps.parcelizeDarwin.runtime) + // Optional, only if you need state preservation on Darwin (Apple) targets + export(deps.parcelizeDarwin.runtime) + } } } - } + } setupSourceSets { val jvm by bundle() diff --git a/sample/shared/shared/build.gradle.kts b/sample/shared/shared/build.gradle.kts index 0a2bfedd0..14de27b58 100644 --- a/sample/shared/shared/build.gradle.kts +++ b/sample/shared/shared/build.gradle.kts @@ -26,23 +26,25 @@ android { } kotlin { - targets - .filterIsInstance() - .filter { it.konanTarget.family == Family.IOS } - .forEach { - it.binaries { - framework { - baseName = "Shared" // Used by app-ios - export(project(":decompose")) + if ("XCODE_VERSION_MAJOR" in System.getenv().keys) { + targets + .filterIsInstance() + .filter { it.konanTarget.family == Family.IOS } + .forEach { + it.binaries { + framework { + baseName = "Shared" // Used by app-ios + export(project(":decompose")) - // Optional, only if you need state preservation on Darwin (Apple) targets - export(deps.essenty.stateKeeper) + // Optional, only if you need state preservation on Darwin (Apple) targets + export(deps.essenty.stateKeeper) - // Optional, only if you need state preservation on Darwin (Apple) targets - export(deps.parcelizeDarwin.runtime) + // Optional, only if you need state preservation on Darwin (Apple) targets + export(deps.parcelizeDarwin.runtime) + } } } - } + } setupSourceSets { val android by bundle() From bbeccadb26882416e1dc7a3da20906b61f025a79 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 8 Dec 2023 23:46:26 +0000 Subject: [PATCH 38/89] Updated Essenty to 2.0.0-alpha01 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 35616f58b..0e8f83074 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -2,7 +2,7 @@ decompose = "2.2.1-compose-experimental" kotlin = "1.9.21" -essenty = "2.0.0-dev03" +essenty = "2.0.0-alpha01" reaktive = "1.2.3" junit = "4.13.2" jetbrainsCompose = "1.6.0-alpha01" From 690b454563112f7ebd5051c6a909dd23da7c387e Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 9 Dec 2023 00:02:51 +0000 Subject: [PATCH 39/89] Bumped version to 3.0.0-alpha01 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 0e8f83074..c3f30bfd9 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.2.1-compose-experimental" +decompose = "3.0.0-alpha01" kotlin = "1.9.21" essenty = "2.0.0-alpha01" reaktive = "1.2.3" From 714ca5d1ea4fe5decf6ea3019af319cbf18dd200 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 10 Dec 2023 20:55:16 +0000 Subject: [PATCH 40/89] Support WebHistoryController on wasmJs by using serialization --- decompose/api/android/decompose.api | 2 +- decompose/api/jvm/decompose.api | 2 +- decompose/build.gradle.kts | 4 + .../stack/webhistory/WebHistoryController.kt | 11 ++- .../DefaultWebHistoryControllerWindow.kt | 27 ++++++ .../com/arkivanov/decompose/TestUtils.kt | 4 + .../router/stack/webhistory/TestHistory.kt | 54 ----------- .../DefaultWebHistoryControllerWindow.kt | 27 ++++++ .../com/arkivanov/decompose/TestUtils.kt | 4 + .../kotlin/com/arkivanov/decompose/Utils.kt | 8 ++ .../decompose/router/stack}/Utils.kt | 2 +- .../webhistory/DefaultWebHistoryController.kt | 95 ++++++++++--------- .../DefaultWebHistoryControllerWindow.kt | 3 + .../router/stack/webhistory/Utils.kt | 4 - .../com/arkivanov/decompose/TestUtils.kt | 3 + .../DefaultWebHistoryControllerTest.kt | 54 ++++++----- .../router/stack/webhistory/TestHistory.kt | 44 +++++++++ .../router/stack/webhistory/TestWindow.kt | 11 ++- .../arkivanov/decompose/sample/app/Main.kt | 4 + .../shared/root/DefaultRootComponent.kt | 1 + 20 files changed, 221 insertions(+), 143 deletions(-) create mode 100644 decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt create mode 100644 decompose/src/jsTest/kotlin/com/arkivanov/decompose/TestUtils.kt delete mode 100644 decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt create mode 100644 decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt create mode 100644 decompose/src/wasmJsTest/kotlin/com/arkivanov/decompose/TestUtils.kt create mode 100644 decompose/src/webMain/kotlin/com/arkivanov/decompose/Utils.kt rename decompose/src/{jsMain/kotlin/com/arkivanov/decompose/router => webMain/kotlin/com/arkivanov/decompose/router/stack}/Utils.kt (94%) rename decompose/src/{jsMain => webMain}/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt (73%) create mode 100644 decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt rename decompose/src/{jsMain => webMain}/kotlin/com/arkivanov/decompose/router/stack/webhistory/Utils.kt (54%) create mode 100644 decompose/src/webTest/kotlin/com/arkivanov/decompose/TestUtils.kt rename decompose/src/{jsTest => webTest}/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt (93%) create mode 100644 decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt rename decompose/src/{jsTest => webTest}/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestWindow.kt (64%) diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 103fee90b..5d886bbdd 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -334,7 +334,7 @@ public final class com/arkivanov/decompose/router/stack/ValueExtKt { } public abstract interface class com/arkivanov/decompose/router/stack/webhistory/WebHistoryController { - public abstract fun attach (Lcom/arkivanov/decompose/router/stack/StackNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V + public abstract fun attach (Lcom/arkivanov/decompose/router/stack/StackNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V public abstract fun getHistoryPaths ()Ljava/util/List; } diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api index 0129efa72..80886d2bb 100644 --- a/decompose/api/jvm/decompose.api +++ b/decompose/api/jvm/decompose.api @@ -318,7 +318,7 @@ public final class com/arkivanov/decompose/router/stack/ValueExtKt { } public abstract interface class com/arkivanov/decompose/router/stack/webhistory/WebHistoryController { - public abstract fun attach (Lcom/arkivanov/decompose/router/stack/StackNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V + public abstract fun attach (Lcom/arkivanov/decompose/router/stack/StackNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V public abstract fun getHistoryPaths ()Ljava/util/List; } diff --git a/decompose/build.gradle.kts b/decompose/build.gradle.kts index b7ac5927d..0081171e5 100644 --- a/decompose/build.gradle.kts +++ b/decompose/build.gradle.kts @@ -71,6 +71,10 @@ kotlin { android.test.dependencies { implementation(deps.robolectric.robolectric) } + + web.main.dependencies { + implementation(deps.jetbrains.kotlinx.kotlinxSerializationJson) + } } } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/WebHistoryController.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/WebHistoryController.kt index 1c6432760..f8d0dc274 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/WebHistoryController.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/WebHistoryController.kt @@ -4,6 +4,7 @@ import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.StackNavigator import com.arkivanov.decompose.value.Value +import kotlinx.serialization.KSerializer /** * Connects the child stack and the Web [History](https://developer.mozilla.org/en-US/docs/Web/API/History) API together. @@ -23,14 +24,16 @@ interface WebHistoryController { * Listens for the [ChildStack] changes and updates the Web [History](https://developer.mozilla.org/en-US/docs/Web/API/History) * accordingly. Also listens for the `History` changes and manipulates the [StackNavigator]. * - * @param navigator a [StackNavigator] that should be manipulated - * @param stack an observable [ChildStack] - * @param getPath a mapper from the configuration to a corresponding Web page path (starting from '/') - * @param getConfiguration a mapper from the Web page path (starting from '/') to a corresponding configuration + * @param navigator a [StackNavigator] that should be manipulated. + * @param stack an observable [ChildStack]. + * @param serializer a [KSerializer] of configurations [C]. + * @param getPath a mapper from the configuration to a corresponding Web page path (starting from '/'). + * @param getConfiguration a mapper from the Web page path (starting from '/') to a corresponding configuration. */ fun attach( navigator: StackNavigator, stack: Value>, + serializer: KSerializer, getPath: (configuration: C) -> String, getConfiguration: (path: String) -> C, ) diff --git a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt b/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt new file mode 100644 index 000000000..faa1c9195 --- /dev/null +++ b/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt @@ -0,0 +1,27 @@ +package com.arkivanov.decompose.router.stack.webhistory + +internal actual class DefaultWebHistoryControllerWindow actual constructor() : DefaultWebHistoryController.Window { + + override val history: DefaultWebHistoryController.History = HistoryImpl() + + override fun setOnPopStateListener(listener: (state: String?) -> Unit) { + kotlinx.browser.window.onpopstate = { listener(it.state?.unsafeCast()) } + } + + private class HistoryImpl : DefaultWebHistoryController.History { + override val state: String? + get() = kotlinx.browser.window.history.state?.unsafeCast() + + override fun go(delta: Int) { + kotlinx.browser.window.history.go(delta = delta) + } + + override fun pushState(data: String, url: String?) { + kotlinx.browser.window.history.pushState(data = data, title = "", url = url) + } + + override fun replaceState(data: String, url: String?) { + kotlinx.browser.window.history.replaceState(data = data, title = "", url = url) + } + } +} diff --git a/decompose/src/jsTest/kotlin/com/arkivanov/decompose/TestUtils.kt b/decompose/src/jsTest/kotlin/com/arkivanov/decompose/TestUtils.kt new file mode 100644 index 000000000..73e426105 --- /dev/null +++ b/decompose/src/jsTest/kotlin/com/arkivanov/decompose/TestUtils.kt @@ -0,0 +1,4 @@ +package com.arkivanov.decompose + +actual fun isNodeJs(): Boolean = + jsTypeOf(kotlinx.browser.window) == "undefined" diff --git a/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt b/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt deleted file mode 100644 index 03ad29657..000000000 --- a/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.arkivanov.decompose.router.stack.webhistory - -import org.w3c.dom.PopStateEvent -import org.w3c.dom.PopStateEventInit -import kotlin.test.assertEquals - -class TestHistory( - private val scheduleOperation: (() -> Unit) -> Unit, - private val onPopState: (PopStateEvent) -> Unit, -) : DefaultWebHistoryController.History { - - private val stack: MutableList = mutableListOf(Entry()) - private var index = 0 - - override val state: Any? get() = stack[index].data - - override fun go(delta: Int) { - scheduleOperation { - index += delta - - onPopState( - PopStateEvent( - type = "PopStateEvent", - eventInitDict = PopStateEventInit(state = stack[index].data) - ) - ) - } - } - - override fun pushState(data: Any?, url: String?) { - while (stack.lastIndex > index) { - stack.removeLast() - } - - stack += Entry(data = data?.serializeAndDeserialize(), url = url) - index++ - } - - override fun replaceState(data: Any?, url: String?) { - stack[index] = Entry(data = data?.serializeAndDeserialize(), url = url) - } - - fun assertStack(urls: List, index: Int = urls.lastIndex) { - assertEquals(urls, stack.map(Entry::url)) - assertEquals(index, this.index) - } - - private fun Any.serializeAndDeserialize(): Any = JSON.parse(JSON.stringify(this)) - - class Entry( - val data: Any? = null, - val url: String? = null, - ) -} diff --git a/decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt b/decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt new file mode 100644 index 000000000..91984956a --- /dev/null +++ b/decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt @@ -0,0 +1,27 @@ +package com.arkivanov.decompose.router.stack.webhistory + +internal actual class DefaultWebHistoryControllerWindow actual constructor() : DefaultWebHistoryController.Window { + + override val history: DefaultWebHistoryController.History = HistoryImpl() + + override fun setOnPopStateListener(listener: (state: String?) -> Unit) { + kotlinx.browser.window.onpopstate = { listener(it.state?.toString()) } + } + + private class HistoryImpl : DefaultWebHistoryController.History { + override val state: String? + get() = kotlinx.browser.window.history.state?.toString() + + override fun go(delta: Int) { + kotlinx.browser.window.history.go(delta = delta) + } + + override fun pushState(data: String, url: String?) { + kotlinx.browser.window.history.pushState(data = data.toJsString(), title = "", url = url) + } + + override fun replaceState(data: String, url: String?) { + kotlinx.browser.window.history.replaceState(data = data.toJsString(), title = "", url = url) + } + } +} diff --git a/decompose/src/wasmJsTest/kotlin/com/arkivanov/decompose/TestUtils.kt b/decompose/src/wasmJsTest/kotlin/com/arkivanov/decompose/TestUtils.kt new file mode 100644 index 000000000..eafa33d8c --- /dev/null +++ b/decompose/src/wasmJsTest/kotlin/com/arkivanov/decompose/TestUtils.kt @@ -0,0 +1,4 @@ +package com.arkivanov.decompose + +actual fun isNodeJs(): Boolean = + false // Decompose doesn't support wasmJs for NodeJs yet diff --git a/decompose/src/webMain/kotlin/com/arkivanov/decompose/Utils.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/Utils.kt new file mode 100644 index 000000000..b79d0ccf4 --- /dev/null +++ b/decompose/src/webMain/kotlin/com/arkivanov/decompose/Utils.kt @@ -0,0 +1,8 @@ +package com.arkivanov.decompose + +import kotlinx.serialization.json.Json + +internal val Json = + Json { + allowStructuredMapKeys = true + } diff --git a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/Utils.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/Utils.kt similarity index 94% rename from decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/Utils.kt rename to decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/Utils.kt index b1fc31f31..758a71b2e 100644 --- a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/Utils.kt +++ b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/Utils.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.router +package com.arkivanov.decompose.router.stack import com.arkivanov.decompose.value.Value import kotlin.math.min diff --git a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt similarity index 73% rename from decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt rename to decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt index bced0ddc8..686458908 100644 --- a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt +++ b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt @@ -1,14 +1,17 @@ package com.arkivanov.decompose.router.stack.webhistory import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.decompose.router.findFirstDifferentIndex +import com.arkivanov.decompose.Json import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.StackNavigator +import com.arkivanov.decompose.router.stack.findFirstDifferentIndex import com.arkivanov.decompose.router.stack.navigate -import com.arkivanov.decompose.router.startsWith -import com.arkivanov.decompose.router.subscribe +import com.arkivanov.decompose.router.stack.startsWith +import com.arkivanov.decompose.router.stack.subscribe import com.arkivanov.decompose.value.Value -import org.w3c.dom.PopStateEvent +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.ListSerializer @ExperimentalDecomposeApi class DefaultWebHistoryController internal constructor( @@ -16,36 +19,44 @@ class DefaultWebHistoryController internal constructor( ) : WebHistoryController { @Suppress("unused") // Public API - constructor() : this(WindowImpl()) + constructor() : this(DefaultWebHistoryControllerWindow()) override val historyPaths: List get() = window.history.getItems().map(PageItem::path) private fun History.getItems(): List = - state?.unsafeCast>()?.asList() ?: emptyList() + state?.let(::deserializeItems) ?: emptyList() private fun History.pushState(items: List) { - pushState(data = items.toTypedArray(), url = items.last().path) + pushState(data = serializeItems(items), url = items.last().path) } private fun History.replaceState(items: List) { - replaceState(data = items.toTypedArray(), url = items.last().path) + replaceState(data = serializeItems(items), url = items.last().path) } + private fun serializeItems(items: List): String = + Json.encodeToString(serializer = PageItem.listSerializer, value = items) + + private fun deserializeItems(json: String): List = + Json.decodeFromString(deserializer = PageItem.listSerializer, string = json) + override fun attach( navigator: StackNavigator, stack: Value>, + serializer: KSerializer, getPath: (configuration: C) -> String, getConfiguration: (path: String) -> C, ) { - val impl = Impl(navigator, stack, getPath, getConfiguration) + val impl = Impl(navigator, stack, serializer, getPath, getConfiguration) impl.init() - window.onPopState = impl::onPopState + window.setOnPopStateListener(impl::onPopState) } private inner class Impl( private val navigator: StackNavigator, private val stack: Value>, + private val serializer: KSerializer, private val getPath: (C) -> String, private val getConfiguration: (String) -> C, ) { @@ -105,8 +116,8 @@ class DefaultWebHistoryController internal constructor( // Some configurations were popped, and one or more configurations were pushed firstDifferentIndex > 0 -> { - window.onPopState = { - window.onPopState = ::onPopState + window.setOnPopStateListener { + window.setOnPopStateListener(::onPopState) // Push new pages to the history for (i in firstDifferentIndex..newConfigurationStack.lastIndex) { @@ -120,8 +131,8 @@ class DefaultWebHistoryController internal constructor( // All configurations were popped, and one or more configurations were pushed else -> { - window.onPopState = { - window.onPopState = ::onPopState + window.setOnPopStateListener { + window.setOnPopStateListener(::onPopState) // Replace the current page with a new one window.history.replaceState(newConfigurationStack[firstDifferentIndex]) @@ -139,14 +150,14 @@ class DefaultWebHistoryController internal constructor( } } - fun onPopState(event: PopStateEvent) { - val newData = event.getData() ?: return - val newConfigurationKey = newData.last().configurationKey + fun onPopState(state: String?) { + val newData = state?.let(::deserializeItems) ?: return + val newConfiguration = deserializeConfiguration(json = newData.last().configurationJson) isStateObserverEnabled = false navigator.navigate { stack -> - val indexInStack = stack.indexOfLast { it.key() == newConfigurationKey } + val indexInStack = stack.indexOfLast { it == newConfiguration } if (indexInStack >= 0) { // History popped, pop from the Router stack.take(indexInStack + 1) @@ -175,49 +186,39 @@ class DefaultWebHistoryController internal constructor( private fun PageItem(configuration: C): PageItem = PageItem( - configurationKey = configuration.key(), + configurationJson = serializeConfiguration(configuration), path = getPath(configuration), ) - private fun PopStateEvent.getData(): Array? = state?.unsafeCast>() + private fun serializeConfiguration(configuration: C): String = + Json.encodeToString(serializer = serializer, value = configuration) + + private fun deserializeConfiguration(json: String): C = + Json.decodeFromString(deserializer = serializer, string = json) } + @Serializable private data class PageItem( - val configurationKey: String, + val configurationJson: String, val path: String, - ) + ) { + companion object { + val listSerializer: KSerializer> = + ListSerializer(serializer()) + } + } internal interface Window { val history: History - var onPopState: ((PopStateEvent) -> Unit)? + + fun setOnPopStateListener(listener: (state: String?) -> Unit) } internal interface History { - val state: Any? + val state: String? fun go(delta: Int) - fun pushState(data: Any?, url: String?) - fun replaceState(data: Any?, url: String?) - } - - private class WindowImpl : Window { - override val history: History = HistoryImpl() - override var onPopState: ((PopStateEvent) -> Unit)? by kotlinx.browser.window::onpopstate - } - - private class HistoryImpl : History { - override val state: Any? by kotlinx.browser.window.history::state - - override fun go(delta: Int) { - kotlinx.browser.window.history.go(delta = delta) - } - - override fun pushState(data: Any?, url: String?) { - kotlinx.browser.window.history.pushState(data = data, title = "", url = url) - } - - override fun replaceState(data: Any?, url: String?) { - kotlinx.browser.window.history.replaceState(data = data, title = "", url = url) - } + fun pushState(data: String, url: String?) + fun replaceState(data: String, url: String?) } } diff --git a/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt new file mode 100644 index 000000000..fbc2b3819 --- /dev/null +++ b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt @@ -0,0 +1,3 @@ +package com.arkivanov.decompose.router.stack.webhistory + +internal expect class DefaultWebHistoryControllerWindow() : DefaultWebHistoryController.Window diff --git a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/Utils.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/Utils.kt similarity index 54% rename from decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/Utils.kt rename to decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/Utils.kt index d19caef93..28d204cd1 100644 --- a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/Utils.kt +++ b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/Utils.kt @@ -4,7 +4,3 @@ import com.arkivanov.decompose.router.stack.ChildStack internal fun ChildStack.configurations(): List = items.map { it.configuration } - -// Just Object#hashCode is not stable between page reloads -internal fun Any.key(): String = - "${this::class.simpleName}_${JSON.stringify(this).hashCode().toString(radix = 36)}" diff --git a/decompose/src/webTest/kotlin/com/arkivanov/decompose/TestUtils.kt b/decompose/src/webTest/kotlin/com/arkivanov/decompose/TestUtils.kt new file mode 100644 index 000000000..832bbc63b --- /dev/null +++ b/decompose/src/webTest/kotlin/com/arkivanov/decompose/TestUtils.kt @@ -0,0 +1,3 @@ +package com.arkivanov.decompose + +expect fun isNodeJs(): Boolean diff --git a/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt similarity index 93% rename from decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt rename to decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt index 4117a8749..3328428fd 100644 --- a/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt +++ b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt @@ -1,10 +1,12 @@ package com.arkivanov.decompose.router.stack.webhistory +import com.arkivanov.decompose.isNodeJs import com.arkivanov.decompose.router.stack.TestStackRouter import com.arkivanov.decompose.router.stack.navigate import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.router.stack.push import com.arkivanov.decompose.router.stack.replaceCurrent +import kotlinx.serialization.Serializable import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -23,7 +25,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_one_config_WHEN_attach_THEN_history_stack_is_correct() { - if (isNode()) { + if (isNodeJs()) { return } @@ -36,7 +38,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_two_configs_WHEN_attach_THEN_history_stack_is_correct() { - if (isNode()) { + if (isNodeJs()) { return } @@ -49,7 +51,7 @@ class DefaultWebHistoryControllerTest { @Test fun WHEN_router_push_THEN_url_pushed_to_history() { - if (isNode()) { + if (isNodeJs()) { return } @@ -64,7 +66,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_two_configs_WHEN_router_pop_THEN_history_changed_to_previous_page() { - if (isNode()) { + if (isNodeJs()) { return } @@ -79,7 +81,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_push_WHEN_router_pop_THEN_history_changed_to_previous_page() { - if (isNode()) { + if (isNodeJs()) { return } @@ -96,7 +98,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_tree_configs_WHEN_router_pop_two_THEN_history_changed_to_previous_page() { - if (isNode()) { + if (isNodeJs()) { return } @@ -111,7 +113,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_push_two_WHEN_router_pop_two_THEN_history_changed_to_previous_page() { - if (isNode()) { + if (isNodeJs()) { return } @@ -128,7 +130,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_push_and_pop_WHEN_router_push_same_config_THEN_history_changed_to_next_page() { - if (isNode()) { + if (isNodeJs()) { return } @@ -147,7 +149,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_push_and_pop_WHEN_router_push_another_config_THEN_history_changed_to_next_page() { - if (isNode()) { + if (isNodeJs()) { return } @@ -166,7 +168,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_b_c_WHEN_navigate_to_a_d_THEN_history_is_a_d_and_d_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -181,7 +183,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_b_c_WHEN_navigate_to_a_d_e_THEN_history_is_a_d_e_and_e_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -196,7 +198,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_b_WHEN_navigate_to_a_c_THEN_history_is_a_c_and_c_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -211,7 +213,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_b_WHEN_navigate_to_a_c_d_THEN_history_is_a_c_d_and_d_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -226,7 +228,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_b_WHEN_navigate_to_c_THEN_history_is_c_b_and_c_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -242,7 +244,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_b_WHEN_navigate_to_c_d_THEN_history_is_c_d_and_d_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -257,7 +259,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_WHEN_navigate_to_b_THEN_history_is_b_and_b_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -272,7 +274,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_WHEN_navigate_to_b_c_THEN_history_is_b_c_and_c_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -289,7 +291,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_and_pushed_b_WHEN_go_back_THEN_stack_is_a() { - if (isNode()) { + if (isNodeJs()) { return } @@ -306,7 +308,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_and_pushed_b_and_go_back_WHEN_go_forward_THEN_history_is_a_b_and_b_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -325,7 +327,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_and_pushed_b_and_go_back_WHEN_go_forward_THEN_stack_is_a_b() { - if (isNode()) { + if (isNodeJs()) { return } @@ -344,7 +346,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_and_pushed_b_and_go_back_and_replace_current_with_c_WHEN_go_forward_THEN_history_is_c_b_and_b_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -365,7 +367,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_and_pushed_b_and_go_back_and_replace_current_with_c_WHEN_go_forward_THEN_stack_is_c_b() { - if (isNode()) { + if (isNodeJs()) { return } @@ -386,7 +388,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_b_and_navigate_to_c_WHEN_go_forward_THEN_stack_is_c_b() { - if (isNode()) { + if (isNodeJs()) { return } @@ -403,7 +405,7 @@ class DefaultWebHistoryControllerTest { @Test fun GIVEN_router_with_initial_stack_of_a_b_and_navigate_to_c_WHEN_go_forward_THEN_history_is_c_b_and_b_is_active() { - if (isNode()) { + if (isNodeJs()) { return } @@ -422,6 +424,7 @@ class DefaultWebHistoryControllerTest { controller.attach( navigator = router, stack = router.stack, + serializer = Config.serializer(), getPath = { "/${it.value}" }, getConfiguration = { Config(it.removePrefix("/").toInt()) }, ) @@ -434,7 +437,6 @@ class DefaultWebHistoryControllerTest { assertEquals(urls.take(index + 1), controller.historyPaths) } - private fun isNode(): Boolean = jsTypeOf(kotlinx.browser.window) == "undefined" - + @Serializable private data class Config(val value: Int) } diff --git a/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt new file mode 100644 index 000000000..e45aa2c77 --- /dev/null +++ b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt @@ -0,0 +1,44 @@ +package com.arkivanov.decompose.router.stack.webhistory + +import kotlin.test.assertEquals + +class TestHistory( + private val scheduleOperation: (() -> Unit) -> Unit, + private val onPopState: (state: String?) -> Unit, +) : DefaultWebHistoryController.History { + + private val stack: MutableList = mutableListOf(Entry()) + private var index = 0 + + override val state: String? get() = stack[index].data + + override fun go(delta: Int) { + scheduleOperation { + index += delta + onPopState(stack[index].data) + } + } + + override fun pushState(data: String, url: String?) { + while (stack.lastIndex > index) { + stack.removeLast() + } + + stack += Entry(data = data, url = url) + index++ + } + + override fun replaceState(data: String, url: String?) { + stack[index] = Entry(data = data, url = url) + } + + fun assertStack(urls: List, index: Int = urls.lastIndex) { + assertEquals(urls, stack.map(Entry::url)) + assertEquals(index, this.index) + } + + class Entry( + val data: String? = null, + val url: String? = null, + ) +} diff --git a/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestWindow.kt b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestWindow.kt similarity index 64% rename from decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestWindow.kt rename to decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestWindow.kt index 7d8a18ff9..e3858358b 100644 --- a/decompose/src/jsTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestWindow.kt +++ b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestWindow.kt @@ -1,19 +1,20 @@ package com.arkivanov.decompose.router.stack.webhistory -import org.w3c.dom.PopStateEvent - class TestWindow : DefaultWebHistoryController.Window { private val pendingOperations = ArrayList<() -> Unit>() - - override var onPopState: ((PopStateEvent) -> Unit)? = null + private var onPopStateListener: ((state: String?) -> Unit)? = null override val history: TestHistory = TestHistory( scheduleOperation = pendingOperations::add, - onPopState = { onPopState?.invoke(it) } + onPopState = { onPopStateListener?.invoke(it) }, ) + override fun setOnPopStateListener(listener: (state: String?) -> Unit) { + onPopStateListener = listener + } + fun runPendingOperations() { val operations = pendingOperations.toList() pendingOperations.clear() diff --git a/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt b/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt index ab30306b1..815b5f6e8 100644 --- a/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt +++ b/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt @@ -5,12 +5,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.window.Window import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.decompose.router.stack.webhistory.DefaultWebHistoryController import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.resume import com.arkivanov.essenty.lifecycle.stop import com.arkivanov.sample.shared.dynamicfeatures.dynamicfeature.DefaultFeatureInstaller import com.arkivanov.sample.shared.root.DefaultRootComponent import com.arkivanov.sample.shared.root.RootContent +import kotlinx.browser.window import org.jetbrains.skiko.wasm.onWasmReady import web.dom.DocumentVisibilityState import web.dom.document @@ -24,6 +26,8 @@ fun main() { DefaultRootComponent( componentContext = DefaultComponentContext(lifecycle = lifecycle), featureInstaller = DefaultFeatureInstaller, + deepLink = DefaultRootComponent.DeepLink.Web(path = window.location.pathname), + webHistoryController = DefaultWebHistoryController(), ) lifecycle.attachToDocument() diff --git a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/root/DefaultRootComponent.kt b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/root/DefaultRootComponent.kt index ac286bcd6..c7a4a93c3 100644 --- a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/root/DefaultRootComponent.kt +++ b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/root/DefaultRootComponent.kt @@ -44,6 +44,7 @@ class DefaultRootComponent( init { webHistoryController?.attach( navigator = navigation, + serializer = Config.serializer(), stack = stack, getPath = ::getPathForConfig, getConfiguration = ::getConfigForPath, From 6d4567f23e4cd20d270e2ccb5a276c6a2c9ed829 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Tue, 12 Dec 2023 14:07:52 +0000 Subject: [PATCH 41/89] Replace emptyStackAnimation with LocalStackAnimation --- .../arkivanov/decompose/extensions/compose/stack/Children.kt | 4 ++-- .../extensions/compose/stack/animation/EmptyStackAnimation.kt | 4 ++-- .../stack/animation/predictiveback/PredictiveBackAnimation.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt index a22e32218..5a0cb67c7 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt @@ -7,8 +7,8 @@ import androidx.compose.runtime.saveable.SaveableStateHolder import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child +import com.arkivanov.decompose.extensions.compose.stack.animation.LocalStackAnimation import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation -import com.arkivanov.decompose.extensions.compose.stack.animation.emptyStackAnimation import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.hashString import com.arkivanov.decompose.router.stack.ChildStack @@ -25,7 +25,7 @@ fun Children( holder.retainStates(stack.getConfigurations()) - val anim = animation ?: emptyStackAnimation() + val anim = animation ?: LocalStackAnimation.current anim(stack = stack, modifier = modifier) { child -> holder.SaveableStateProvider(child.configuration.hashString()) { diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt index 55cfc2714..6deefaafc 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt @@ -2,13 +2,13 @@ package com.arkivanov.decompose.extensions.compose.stack.animation import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable +import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child import com.arkivanov.decompose.router.stack.ChildStack -internal fun emptyStackAnimation(): StackAnimation = - EmptyStackAnimation() +val LocalStackAnimation = compositionLocalOf> { EmptyStackAnimation() } /* * Can't be anonymous. See: * https://github.com/JetBrains/compose-jb/issues/2688 diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt index 808b51fe5..f15118b84 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt @@ -15,8 +15,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.decompose.extensions.compose.stack.animation.LocalStackAnimation import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation -import com.arkivanov.decompose.extensions.compose.stack.animation.emptyStackAnimation import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.essenty.backhandler.BackEvent import com.arkivanov.essenty.backhandler.BackHandler @@ -48,7 +48,7 @@ fun predictiveBackAnimation( ): StackAnimation = PredictiveBackAnimation( backHandler = backHandler, - animation = fallbackAnimation ?: emptyStackAnimation(), + animation = fallbackAnimation ?: LocalStackAnimation.current, selector = selector, onBack = onBack, ) From 4b65bc72c8df48e5d0705685c52401de54b44428 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Tue, 12 Dec 2023 16:45:34 +0000 Subject: [PATCH 42/89] Fix compilation --- .../extensions/compose/stack/Children.kt | 2 +- .../stack/animation/EmptyStackAnimation.kt | 15 ++++++++++++++- .../predictiveback/PredictiveBackAnimation.kt | 7 ++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt index 5a0cb67c7..9f497cf78 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt @@ -25,7 +25,7 @@ fun Children( holder.retainStates(stack.getConfigurations()) - val anim = animation ?: LocalStackAnimation.current + val anim = animation ?: LocalStackAnimation.current.invoke() anim(stack = stack, modifier = modifier) { child -> holder.SaveableStateProvider(child.configuration.hashString()) { diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt index 6deefaafc..f5d38d046 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt @@ -8,7 +8,20 @@ import com.arkivanov.decompose.Child import com.arkivanov.decompose.router.stack.ChildStack -val LocalStackAnimation = compositionLocalOf> { EmptyStackAnimation() } +val LocalStackAnimation = compositionLocalOf { + object : DefaultStackAnimationProvider { + override fun invoke(): StackAnimation = + emptyStackAnimation() + } +} + +interface DefaultStackAnimationProvider { + operator fun invoke(): StackAnimation +} + +private fun emptyStackAnimation(): StackAnimation = + EmptyStackAnimation() + /* * Can't be anonymous. See: * https://github.com/JetBrains/compose-jb/issues/2688 diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt index f15118b84..a6e4a460e 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt @@ -48,7 +48,7 @@ fun predictiveBackAnimation( ): StackAnimation = PredictiveBackAnimation( backHandler = backHandler, - animation = fallbackAnimation ?: LocalStackAnimation.current, + animation = fallbackAnimation, selector = selector, onBack = onBack, ) @@ -56,7 +56,7 @@ fun predictiveBackAnimation( @OptIn(ExperimentalDecomposeApi::class) private class PredictiveBackAnimation( private val backHandler: BackHandler, - private val animation: StackAnimation, + private val animation: StackAnimation?, private val selector: (BackEvent, exitChild: Child.Created, enterChild: Child.Created) -> PredictiveBackAnimatable, private val onBack: () -> Unit, ) : StackAnimation { @@ -64,6 +64,7 @@ private class PredictiveBackAnimation( @Composable override fun invoke(stack: ChildStack, modifier: Modifier, content: @Composable (child: Child.Created) -> Unit) { var activeConfigurations: Set by remember { mutableStateOf(emptySet()) } + val fallBackAnimationOrDefault = animation ?: LocalStackAnimation.current.invoke() val childContent = remember(content) { @@ -98,7 +99,7 @@ private class PredictiveBackAnimation( Box(modifier = modifier) { items.forEach { item -> key(item.key) { - animation( + fallBackAnimationOrDefault( stack = item.stack, modifier = Modifier.fillMaxSize().then(item.modifier), content = childContent, From dbaabda4aa1215b9d142814972e646c7c16e69b9 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Tue, 12 Dec 2023 22:41:49 +0000 Subject: [PATCH 43/89] Updated Essenty to 2.0.0-alpha01 --- deps.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps.versions.toml b/deps.versions.toml index c3f30bfd9..44114e204 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,8 +1,8 @@ [versions] -decompose = "3.0.0-alpha01" +decompose = "3.0.0-alpha02" kotlin = "1.9.21" -essenty = "2.0.0-alpha01" +essenty = "2.0.0-alpha02" reaktive = "1.2.3" junit = "4.13.2" jetbrainsCompose = "1.6.0-alpha01" From 683f8c60251b9201b9444c8ce069f8ff1a1e092a Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Tue, 12 Dec 2023 23:45:10 +0000 Subject: [PATCH 44/89] Updated check-publication --- tools/check-publication/build.gradle.kts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tools/check-publication/build.gradle.kts b/tools/check-publication/build.gradle.kts index 4c5a089e2..e4040fb2c 100644 --- a/tools/check-publication/build.gradle.kts +++ b/tools/check-publication/build.gradle.kts @@ -1,4 +1,6 @@ import com.arkivanov.gradle.bundle +import com.arkivanov.gradle.dependsOn +import com.arkivanov.gradle.plus import com.arkivanov.gradle.setupMultiplatform import com.arkivanov.gradle.setupSourceSets @@ -29,6 +31,13 @@ kotlin { setupSourceSets { val android by bundle() val jvm by bundle() + val compose by bundle() + val js by bundle() + val wasmJs by bundle() + + compose dependsOn common + android dependsOn compose + (android + jvm + js + wasmJs + iosSet + macosSet) dependsOn compose common.main.dependencies { implementation("com.arkivanov.decompose:decompose:$version") @@ -36,11 +45,10 @@ kotlin { android.main.dependencies { implementation("com.arkivanov.decompose:extensions-android:$version") - implementation("com.arkivanov.decompose:extensions-compose-jetbrains:$version") } - jvm.main.dependencies { - implementation("com.arkivanov.decompose:extensions-compose-jetbrains:$version") + compose.main.dependencies { + implementation("com.arkivanov.decompose:extensions-compose:$version") } } } From b86451771eb2f9775e4cbf666746b55abce5979f Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Fri, 15 Dec 2023 19:10:58 +0000 Subject: [PATCH 45/89] Add ability to StackAnimationProvider use nullability --- .../api/android/extensions-compose.api | 8 ++++++++ extensions-compose/api/jvm/extensions-compose.api | 8 ++++++++ .../extensions/compose/stack/Children.kt | 6 ++++-- .../stack/animation/EmptyStackAnimation.kt | 14 +------------- .../stack/animation/StackAnimationProvider.kt | 15 +++++++++++++++ .../predictiveback/PredictiveBackAnimation.kt | 8 +++++--- 6 files changed, 41 insertions(+), 18 deletions(-) create mode 100644 extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProvider.kt diff --git a/extensions-compose/api/android/extensions-compose.api b/extensions-compose/api/android/extensions-compose.api index 1ffa4dc63..4894346d2 100644 --- a/extensions-compose/api/android/extensions-compose.api +++ b/extensions-compose/api/android/extensions-compose.api @@ -93,6 +93,14 @@ public final class com/arkivanov/decompose/extensions/compose/stack/animation/St public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; } +public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProvider { + public abstract fun provide ()Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProviderKt { + public static final fun getLocalStackAnimationProvider ()Landroidx/compose/runtime/ProvidableCompositionLocal; +} + public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator { public abstract fun invoke (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V } diff --git a/extensions-compose/api/jvm/extensions-compose.api b/extensions-compose/api/jvm/extensions-compose.api index 3bd776c26..248ab13cb 100644 --- a/extensions-compose/api/jvm/extensions-compose.api +++ b/extensions-compose/api/jvm/extensions-compose.api @@ -105,6 +105,14 @@ public final class com/arkivanov/decompose/extensions/compose/stack/animation/St public static synthetic fun stackAnimation$default (ZLkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; } +public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProvider { + public abstract fun provide ()Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProviderKt { + public static final fun getLocalStackAnimationProvider ()Landroidx/compose/runtime/ProvidableCompositionLocal; +} + public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator { public abstract fun invoke (Lcom/arkivanov/decompose/extensions/compose/stack/animation/Direction;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;I)V } diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt index 9f497cf78..81b8314e7 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/Children.kt @@ -7,8 +7,9 @@ import androidx.compose.runtime.saveable.SaveableStateHolder import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child -import com.arkivanov.decompose.extensions.compose.stack.animation.LocalStackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.LocalStackAnimationProvider import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.emptyStackAnimation import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.hashString import com.arkivanov.decompose.router.stack.ChildStack @@ -25,7 +26,8 @@ fun Children( holder.retainStates(stack.getConfigurations()) - val anim = animation ?: LocalStackAnimation.current.invoke() + val animationProvider = LocalStackAnimationProvider.current + val anim = animation ?: remember(animationProvider, animationProvider::provide) ?: emptyStackAnimation() anim(stack = stack, modifier = modifier) { child -> holder.SaveableStateProvider(child.configuration.hashString()) { diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt index f5d38d046..22c2be508 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/EmptyStackAnimation.kt @@ -7,19 +7,7 @@ import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child import com.arkivanov.decompose.router.stack.ChildStack - -val LocalStackAnimation = compositionLocalOf { - object : DefaultStackAnimationProvider { - override fun invoke(): StackAnimation = - emptyStackAnimation() - } -} - -interface DefaultStackAnimationProvider { - operator fun invoke(): StackAnimation -} - -private fun emptyStackAnimation(): StackAnimation = +internal fun emptyStackAnimation(): StackAnimation = EmptyStackAnimation() /* diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProvider.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProvider.kt new file mode 100644 index 000000000..1d9173bb1 --- /dev/null +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProvider.kt @@ -0,0 +1,15 @@ +package com.arkivanov.decompose.extensions.compose.stack.animation + +import androidx.compose.runtime.compositionLocalOf + +interface StackAnimationProvider { + fun provide(): StackAnimation? +} + +val LocalStackAnimationProvider = compositionLocalOf { + object : StackAnimationProvider { + override fun provide(): StackAnimation = + emptyStackAnimation() + } +} + diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt index a6e4a460e..e9a4fe854 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt @@ -15,8 +15,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.decompose.extensions.compose.stack.animation.LocalStackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.LocalStackAnimationProvider import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.emptyStackAnimation import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.essenty.backhandler.BackEvent import com.arkivanov.essenty.backhandler.BackHandler @@ -64,7 +65,8 @@ private class PredictiveBackAnimation( @Composable override fun invoke(stack: ChildStack, modifier: Modifier, content: @Composable (child: Child.Created) -> Unit) { var activeConfigurations: Set by remember { mutableStateOf(emptySet()) } - val fallBackAnimationOrDefault = animation ?: LocalStackAnimation.current.invoke() + val animationProvider = LocalStackAnimationProvider.current + val fallBackAnimation = animation ?: remember(animationProvider, animationProvider::provide) ?: emptyStackAnimation() val childContent = remember(content) { @@ -99,7 +101,7 @@ private class PredictiveBackAnimation( Box(modifier = modifier) { items.forEach { item -> key(item.key) { - fallBackAnimationOrDefault( + fallBackAnimation( stack = item.stack, modifier = Modifier.fillMaxSize().then(item.modifier), content = childContent, From 67ccf1dba2622c12f52fea84b81cb2a172f8b3d7 Mon Sep 17 00:00:00 2001 From: Nikita Kulikov Date: Sat, 16 Dec 2023 10:20:45 +0000 Subject: [PATCH 46/89] Return by default null --- .../compose/stack/animation/StackAnimationProvider.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProvider.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProvider.kt index 1d9173bb1..c4fe768a7 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProvider.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/StackAnimationProvider.kt @@ -8,8 +8,7 @@ interface StackAnimationProvider { val LocalStackAnimationProvider = compositionLocalOf { object : StackAnimationProvider { - override fun provide(): StackAnimation = - emptyStackAnimation() + override fun provide(): StackAnimation? = null } } From c86f9387f013d230d52cec2222dfb613df0ae439 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 16 Dec 2023 11:44:02 +0000 Subject: [PATCH 47/89] Bumped version to 3.0.0-alpha03 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 44114e204..a20b5b21d 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "3.0.0-alpha02" +decompose = "3.0.0-alpha03" kotlin = "1.9.21" essenty = "2.0.0-alpha02" reaktive = "1.2.3" From 138508ae0008aa6d1c20348fa99f9abc81445b2c Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 23 Dec 2023 21:31:19 +0000 Subject: [PATCH 48/89] Bumped version to 3.0.0-alpha04 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index a20b5b21d..14f75c813 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "3.0.0-alpha03" +decompose = "3.0.0-alpha04" kotlin = "1.9.21" essenty = "2.0.0-alpha02" reaktive = "1.2.3" From 2b92d76f9a99ffc7e7f3cb8e6dc71ee64996b50d Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 23 Dec 2023 21:32:09 +0000 Subject: [PATCH 49/89] Bumped version to 2.2.2-compose-experimental --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 18f2adbe5..33e831e96 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "2.2.1-compose-experimental" +decompose = "2.2.2-compose-experimental" kotlin = "1.9.21" essenty = "1.3.0" parcelizeDarwin = "0.2.3" From 20941bf3e28e475a009def17ca9e8fa06c148988 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Mon, 8 Jan 2024 21:04:28 +0000 Subject: [PATCH 50/89] Added StackNavigator#pushToFront extension function --- decompose/api/android/decompose.api | 2 + decompose/api/jvm/decompose.api | 2 + .../router/stack/StackNavigatorExt.kt | 20 +++++++++ .../router/stack/RouterPushToFrontTest.kt | 44 +++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterPushToFrontTest.kt diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 5d886bbdd..1a0d3e6a1 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -321,6 +321,8 @@ public final class com/arkivanov/decompose/router/stack/StackNavigatorExtKt { public static synthetic fun push$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V public static final fun pushNew (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V public static synthetic fun pushNew$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V + public static final fun pushToFront (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V + public static synthetic fun pushToFront$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V public static final fun replaceAll (Lcom/arkivanov/decompose/router/stack/StackNavigator;[Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V public static synthetic fun replaceAll$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;[Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V public static final fun replaceCurrent (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api index 80886d2bb..0fa02f181 100644 --- a/decompose/api/jvm/decompose.api +++ b/decompose/api/jvm/decompose.api @@ -305,6 +305,8 @@ public final class com/arkivanov/decompose/router/stack/StackNavigatorExtKt { public static synthetic fun push$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V public static final fun pushNew (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)V public static synthetic fun pushNew$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V + public static final fun pushToFront (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V + public static synthetic fun pushToFront$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V public static final fun replaceAll (Lcom/arkivanov/decompose/router/stack/StackNavigator;[Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V public static synthetic fun replaceAll$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;[Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V public static final fun replaceCurrent (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt index 5839087bd..7cbd82f92 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt @@ -44,6 +44,26 @@ fun StackNavigator.pushNew( ) } +/** + * Pushes the provided [configuration] to the top of the stack, + * removing the [configuration] from the back stack, if any. + * + * This API works similar to [bringToFront], except it compares configurations + * by equality rather than by configuration class. + * + * @param onComplete called when the navigation is finished (either synchronously or asynchronously). + */ +@ExperimentalDecomposeApi +fun StackNavigator.pushToFront( + configuration: C, + onComplete: () -> Unit = {}, +) { + navigate( + transformer = { stack -> stack - configuration + configuration }, + onComplete = { _, _ -> onComplete() }, + ) +} + /** * Pops the latest configuration at the top of the stack. * diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterPushToFrontTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterPushToFrontTest.kt new file mode 100644 index 000000000..843127355 --- /dev/null +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterPushToFrontTest.kt @@ -0,0 +1,44 @@ +package com.arkivanov.decompose.router.stack + +import kotlin.test.Test +import kotlin.test.assertEquals + +@Suppress("TestFunctionName") +class RouterPushToFrontTest { + + @Test + fun WHEN_does_not_contain_THEN_new_configuration_pushed() { + val navigator = TestStackNavigator(listOf(1, 2)) + + navigator.pushToFront(3) + + assertEquals(listOf(1, 2, 3), navigator.configurations) + } + + @Test + fun WHEN_exists_with_same_value_at_start_THEN_existing_configuration_moved_to_end() { + val navigator = TestStackNavigator(listOf(1, 2, 3)) + + navigator.pushToFront(1) + + assertEquals(listOf(2, 3, 1), navigator.configurations) + } + + @Test + fun WHEN_exists_with_same_value_in_the_middle_THEN_existing_configuration_moved_to_end() { + val navigator = TestStackNavigator(listOf(1, 2, 3)) + + navigator.pushToFront(2) + + assertEquals(listOf(1, 3, 2), navigator.configurations) + } + + @Test + fun WHEN_exists_with_same_value_at_the_end_THEN_not_changed() { + val navigator = TestStackNavigator(listOf(1, 2, 3)) + + navigator.pushToFront(3) + + assertEquals(listOf(1, 2, 3), navigator.configurations) + } +} From 6b8f21f116e0fb378bb6ab7f0fb11d0abe3320cc Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Tue, 9 Jan 2024 21:28:36 +0000 Subject: [PATCH 51/89] Added discardSavedState and isStateSavingAllowed to retainedComponent --- decompose/api/android/decompose.api | 8 +- .../arkivanov/decompose/RetainedComponent.kt | 31 ++- .../arkivanov/decompose/AndroidTestUtils.kt | 16 ++ .../DefaultComponentContextBuilderTest.kt | 113 +++++----- .../decompose/RetainedComponentTest.kt | 205 ++++++++++++++++++ .../com/arkivanov/decompose/TestOwner.kt | 57 +++++ 6 files changed, 367 insertions(+), 63 deletions(-) create mode 100644 decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/AndroidTestUtils.kt create mode 100644 decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/RetainedComponentTest.kt create mode 100644 decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/TestOwner.kt diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 1a0d3e6a1..54a9c63a3 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -70,10 +70,10 @@ public abstract interface annotation class com/arkivanov/decompose/InternalDecom } public final class com/arkivanov/decompose/RetainedComponentKt { - public static final fun retainedComponent (Landroidx/activity/ComponentActivity;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public static final fun retainedComponent (Landroidx/fragment/app/Fragment;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public static synthetic fun retainedComponent$default (Landroidx/activity/ComponentActivity;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object; - public static synthetic fun retainedComponent$default (Landroidx/fragment/app/Fragment;Ljava/lang/String;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun retainedComponent (Landroidx/activity/ComponentActivity;Ljava/lang/String;ZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static final fun retainedComponent (Landroidx/fragment/app/Fragment;Ljava/lang/String;ZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static synthetic fun retainedComponent$default (Landroidx/activity/ComponentActivity;Ljava/lang/String;ZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object; + public static synthetic fun retainedComponent$default (Landroidx/fragment/app/Fragment;Ljava/lang/String;ZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object; } public final class com/arkivanov/decompose/UtilsKt { diff --git a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt index 139b3ecad..017029b8d 100644 --- a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt +++ b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt @@ -25,6 +25,7 @@ import com.arkivanov.essenty.lifecycle.subscribe import com.arkivanov.essenty.statekeeper.SerializableContainer import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher import com.arkivanov.essenty.statekeeper.stateKeeper +import kotlinx.serialization.builtins.serializer /** * Returns (creating if needed) a component that is retained over configuration changes. @@ -34,17 +35,26 @@ import com.arkivanov.essenty.statekeeper.stateKeeper * * @param key a key of the component, must be unique within the `Activity`. * @param handleBackButton a flag that determines whether back button handling is enabled or not, default is `true`. + * @param discardSavedState a flag indicating whether any previously saved state should be discarded or not, + * default value is `false`. Can be useful for handling deep links in `onCreate`, so that the navigation state + * is not restored and initial state from the deep link is applied instead. + * @param isStateSavingAllowed called before saving the state. When `true` then the state will be saved, + * otherwise it won't. Default value is `true`. * @param factory a function that returns a new instance of the component. */ @ExperimentalDecomposeApi fun ComponentActivity.retainedComponent( key: String = "RootRetainedComponent", handleBackButton: Boolean = true, + discardSavedState: Boolean = false, + isStateSavingAllowed: () -> Boolean = { true }, factory: (ComponentContext) -> T, ): T = retainedComponent( key = key, onBackPressedDispatcher = if (handleBackButton) onBackPressedDispatcher else null, + discardSavedState = discardSavedState, + isStateSavingAllowed = isStateSavingAllowed, isChangingConfigurations = ::isChangingConfigurations, factory = factory, ) @@ -57,30 +67,43 @@ fun ComponentActivity.retainedComponent( * * @param key a key of the component, must be unique within the `Fragment`. * @param handleBackButton a flag that determines whether back button handling is enabled or not, default is `true`. + * @param discardSavedState a flag indicating whether any previously saved state should be discarded or not, + * default value is `false`. Can be useful for handling deep links in `onCreate`, so that the navigation state + * is not restored and initial state from the deep link is applied instead. + * @param isStateSavingAllowed called before saving the state. When `true` then the state will be saved, + * otherwise it won't. Default value is `true`. * @param factory a function that returns a new instance of the component. */ @ExperimentalDecomposeApi fun Fragment.retainedComponent( key: String = "RootRetainedComponent", handleBackButton: Boolean = true, + discardSavedState: Boolean = false, + isStateSavingAllowed: () -> Boolean = { true }, factory: (ComponentContext) -> T, ): T = retainedComponent( key = key, onBackPressedDispatcher = if (handleBackButton) requireActivity().onBackPressedDispatcher else null, + discardSavedState = discardSavedState, + isStateSavingAllowed = isStateSavingAllowed, isChangingConfigurations = { activity?.isChangingConfigurations ?: false }, factory = factory, ) -private fun O.retainedComponent( +internal fun O.retainedComponent( key: String, onBackPressedDispatcher: OnBackPressedDispatcher?, + discardSavedState: Boolean, + isStateSavingAllowed: () -> Boolean, isChangingConfigurations: () -> Boolean, factory: (ComponentContext) -> T, ): T where O : LifecycleOwner, O : SavedStateRegistryOwner, O : ViewModelStoreOwner { val lifecycle = essentyLifecycle() - val stateKeeper = stateKeeper() - val instanceKeeper = instanceKeeper() + val stateKeeper = stateKeeper(discardSavedState = discardSavedState, isSavingAllowed = isStateSavingAllowed) + val marker = stateKeeper.consume(key = KEY_STATE_MARKER, strategy = String.serializer()) + stateKeeper.register(key = KEY_STATE_MARKER, strategy = String.serializer()) { "marker" } + val instanceKeeper = instanceKeeper(discardRetainedInstances = marker == null) check(!stateKeeper.isRegistered(key = key)) { "Another retained component is already registered with the key: $key" } @@ -124,6 +147,8 @@ private fun O.retainedComponent( return holder.component } +private const val KEY_STATE_MARKER = "RetainedComponent_state_marker" + private class DelegateOnBackPressedCallback( private val dispatcher: OnBackPressedDispatcher, ) : OnBackPressedCallback(enabled = dispatcher.hasEnabledCallbacks()) { diff --git a/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/AndroidTestUtils.kt b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/AndroidTestUtils.kt new file mode 100644 index 000000000..5c7069e36 --- /dev/null +++ b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/AndroidTestUtils.kt @@ -0,0 +1,16 @@ +package com.arkivanov.decompose + +import com.arkivanov.essenty.lifecycle.Lifecycle +import com.arkivanov.essenty.lifecycle.subscribe + +fun Lifecycle.logEvents(): MutableList = + ArrayList().apply { + subscribe( + onCreate = { add("onCreate") }, + onStart = { add("onStart") }, + onResume = { add("onResume") }, + onPause = { add("onPause") }, + onStop = { add("onStop") }, + onDestroy = { add("onDestroy") }, + ) + } diff --git a/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilderTest.kt b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilderTest.kt index ae33f473c..117415bab 100644 --- a/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilderTest.kt +++ b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/DefaultComponentContextBuilderTest.kt @@ -1,23 +1,12 @@ package com.arkivanov.decompose -import android.os.Bundle -import android.os.Parcel -import androidx.activity.OnBackPressedDispatcher -import androidx.activity.OnBackPressedDispatcherOwner -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.LifecycleRegistry -import androidx.lifecycle.ViewModelStore -import androidx.lifecycle.ViewModelStoreOwner -import androidx.savedstate.SavedStateRegistry -import androidx.savedstate.SavedStateRegistryController -import androidx.savedstate.SavedStateRegistryOwner import com.arkivanov.decompose.router.TestInstance import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.instancekeeper.getOrCreate import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import kotlin.test.Test +import kotlin.test.assertContentEquals import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotSame @@ -30,12 +19,59 @@ import kotlin.test.assertTrue class DefaultComponentContextBuilderTest { @Test - fun saves_and_restores_state() { + fun WHEN_created_THEN_lifecycle_resumed() { + val owner = TestOwner() + val ctx = owner.defaultComponentContext() + val events = ctx.lifecycle.logEvents() + + assertContentEquals(listOf("onCreate", "onStart", "onResume"), events) + } + + @Test + fun WHEN_recreated_THEN_old_lifecycle_destroyed() { + var owner = TestOwner() + val ctx = owner.defaultComponentContext() + val events = ctx.lifecycle.logEvents() + events.clear() + + owner = owner.recreate(isChangingConfigurations = false) + owner.defaultComponentContext() + + assertContentEquals(listOf("onPause", "onStop", "onDestroy"), events) + } + + @Test + fun WHEN_recreated_THEN_new_lifecycle_resumed() { + var owner = TestOwner() + owner.defaultComponentContext() + + owner = owner.recreate(isChangingConfigurations = false) + val ctx = owner.defaultComponentContext() + val events = ctx.lifecycle.logEvents() + + assertContentEquals(listOf("onCreate", "onStart", "onResume"), events) + } + + @Test + fun WHEN_recreated_THEN_saves_and_restores_state() { var owner = TestOwner() var ctx = owner.defaultComponentContext() ctx.stateKeeper.register(key = "key") { "saved_state" } - owner = owner.recreate() + owner = owner.recreate(isChangingConfigurations = false) + ctx = owner.defaultComponentContext() + val restoredState = ctx.stateKeeper.consume(key = "key") + + assertEquals("saved_state", restoredState) + } + + @Test + fun WHEN_configuration_changed_THEN_saves_and_restores_state() { + var owner = TestOwner() + var ctx = owner.defaultComponentContext() + ctx.stateKeeper.register(key = "key") { "saved_state" } + + owner = owner.recreate(isChangingConfigurations = true) ctx = owner.defaultComponentContext() val restoredState = ctx.stateKeeper.consume(key = "key") @@ -56,7 +92,7 @@ class DefaultComponentContextBuilderTest { } @Test - fun GIVEN_isStateSavingAllowed_is_false_on_save_THEN_state_not_saved() { + fun GIVEN_isStateSavingAllowed_is_false_on_save_WHEN_configuration_changed_THEN_state_not_saved() { var owner = TestOwner() var ctx = owner.defaultComponentContext(isStateSavingAllowed = { false }) ctx.stateKeeper.register(key = "key") { "saved_state" } @@ -69,7 +105,7 @@ class DefaultComponentContextBuilderTest { } @Test - fun GIVEN_isStateSavingAllowed_is_false_on_save_THEN_instances_not_retained() { + fun GIVEN_isStateSavingAllowed_is_false_on_save_WHEN_configuration_changed_THEN_instances_not_retained() { var owner = TestOwner() var ctx = owner.defaultComponentContext(isStateSavingAllowed = { false }) val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) @@ -82,9 +118,9 @@ class DefaultComponentContextBuilderTest { } @Test - fun GIVEN_isStateSavingAllowed_is_false_on_save_THEN_old_instances_destroyed() { + fun GIVEN_isStateSavingAllowed_is_false_on_save_WHEN_configuration_changed_THEN_old_instances_destroyed() { var owner = TestOwner() - var ctx = owner.defaultComponentContext(isStateSavingAllowed = { false }) + val ctx = owner.defaultComponentContext(isStateSavingAllowed = { false }) val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) owner = owner.recreate() @@ -94,7 +130,7 @@ class DefaultComponentContextBuilderTest { } @Test - fun GIVEN_discardSavedState_is_true_on_restore_THEN_discards_saved_state() { + fun WHEN_configuration_changed_and_discardSavedState_is_true_on_restore_THEN_discards_saved_state() { var owner = TestOwner() var ctx = owner.defaultComponentContext() ctx.stateKeeper.register(key = "key") { "saved_state" } @@ -107,7 +143,7 @@ class DefaultComponentContextBuilderTest { } @Test - fun GIVEN_discardSavedState_is_true_on_restore_THEN_instances_not_retained() { + fun WHEN_configuration_changed_and_discardSavedState_is_true_on_restore_THEN_instances_not_retained() { var owner = TestOwner() var ctx = owner.defaultComponentContext() val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) @@ -120,7 +156,7 @@ class DefaultComponentContextBuilderTest { } @Test - fun GIVEN_discardSavedState_is_true_on_restore_THEN_old_instances_destroyed() { + fun WHEN_configuration_changed_and_discardSavedState_is_true_on_restore_THEN_old_instances_destroyed() { var owner = TestOwner() val ctx = owner.defaultComponentContext() val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) @@ -154,39 +190,4 @@ class DefaultComponentContextBuilderTest { assertFalse(isCalled) } - - private class TestOwner( - savedState: Bundle = Bundle(), - override val viewModelStore: ViewModelStore = ViewModelStore(), - ) : LifecycleOwner, SavedStateRegistryOwner, ViewModelStoreOwner, OnBackPressedDispatcherOwner { - private val savedStateRegistryController: SavedStateRegistryController = SavedStateRegistryController.create(this) - override val lifecycle: Lifecycle = LifecycleRegistry(this) - override val savedStateRegistry: SavedStateRegistry get() = savedStateRegistryController.savedStateRegistry - override val onBackPressedDispatcher: OnBackPressedDispatcher = OnBackPressedDispatcher() - - init { - savedStateRegistryController.performRestore(savedState) - } - - fun recreate(): TestOwner { - val bundle = Bundle() - savedStateRegistryController.performSave(bundle) - - return TestOwner(savedState = bundle.parcelize().deparcelize(), viewModelStore = viewModelStore) - } - - private fun Bundle.parcelize(): ByteArray { - val parcel = Parcel.obtain() - parcel.writeBundle(this) - return parcel.marshall() - } - - private fun ByteArray.deparcelize(): Bundle { - val parcel = Parcel.obtain() - parcel.unmarshall(this, 0, size) - parcel.setDataPosition(0) - - return requireNotNull(parcel.readBundle()) - } - } } diff --git a/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/RetainedComponentTest.kt b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/RetainedComponentTest.kt new file mode 100644 index 000000000..7ea834162 --- /dev/null +++ b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/RetainedComponentTest.kt @@ -0,0 +1,205 @@ +package com.arkivanov.decompose + +import com.arkivanov.decompose.router.TestInstance +import com.arkivanov.essenty.backhandler.BackCallback +import com.arkivanov.essenty.instancekeeper.getOrCreate +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotSame +import kotlin.test.assertNull +import kotlin.test.assertSame +import kotlin.test.assertTrue + +@Suppress("TestFunctionName") +@RunWith(RobolectricTestRunner::class) +class RetainedComponentTest { + + @Test + fun WHEN_created_THEN_lifecycle_resumed() { + val owner = TestOwner() + val ctx = owner.retainedComponent() + val events = ctx.lifecycle.logEvents() + + assertContentEquals(listOf("onCreate", "onStart", "onResume"), events) + } + + @Test + fun WHEN_recreated_THEN_old_lifecycle_destroyed() { + var owner = TestOwner() + val ctx = owner.retainedComponent() + val events = ctx.lifecycle.logEvents() + events.clear() + + owner = owner.recreate(isChangingConfigurations = false) + owner.retainedComponent() + + assertContentEquals(listOf("onPause", "onStop", "onDestroy"), events) + } + + @Test + fun WHEN_recreated_THEN_new_lifecycle_resumed() { + var owner = TestOwner() + owner.retainedComponent() + + owner = owner.recreate(isChangingConfigurations = false) + val ctx = owner.retainedComponent() + val events = ctx.lifecycle.logEvents() + + assertContentEquals(listOf("onCreate", "onStart", "onResume"), events) + } + + @Test + fun WHEN_configuration_changed_THEN_lifecycle_not_called() { + var owner = TestOwner() + val ctx = owner.retainedComponent(isChangingConfigurations = { true }) + val events = ctx.lifecycle.logEvents() + events.clear() + + owner = owner.recreate(isChangingConfigurations = true) + owner.retainedComponent() + + assertContentEquals(emptyList(), events) + } + + @Test + fun WHEN_recreated_THEN_saves_and_restores_state() { + var owner = TestOwner() + var ctx = owner.retainedComponent() + ctx.stateKeeper.register(key = "key") { "saved_state" } + + owner = owner.recreate(isChangingConfigurations = false) + ctx = owner.retainedComponent() + val restoredState = ctx.stateKeeper.consume(key = "key") + + assertEquals("saved_state", restoredState) + } + + @Test + fun retains_instances() { + var owner = TestOwner() + val ctx1 = owner.retainedComponent() + + owner = owner.recreate() + val ctx2 = owner.retainedComponent() + + assertSame(ctx1, ctx2) + } + + @Test + fun GIVEN_isStateSavingAllowed_is_false_on_save_WHEN_recreated_THEN_state_not_saved() { + var owner = TestOwner() + var ctx = owner.retainedComponent(isStateSavingAllowed = { false }) + ctx.stateKeeper.register(key = "key") { "saved_state" } + + owner = owner.recreate(isChangingConfigurations = false) + ctx = owner.retainedComponent() + val restoredState = ctx.stateKeeper.consume(key = "key") + + assertNull(restoredState) + } + + @Test + fun GIVEN_isStateSavingAllowed_is_false_on_save_WHEN_configuration_changed_THEN_instances_not_retained() { + var owner = TestOwner() + var ctx = owner.retainedComponent(isStateSavingAllowed = { false }) + val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + owner = owner.recreate(isChangingConfigurations = true) + ctx = owner.retainedComponent() + val instance2 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + assertNotSame(instance1, instance2) + } + + @Test + fun GIVEN_isStateSavingAllowed_is_false_on_save_WHEN_configuration_changed_THEN_old_instances_destroyed() { + var owner = TestOwner() + val ctx = owner.retainedComponent(isStateSavingAllowed = { false }) + val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + owner = owner.recreate(isChangingConfigurations = true) + owner.retainedComponent() + + assertTrue(instance1.isDestroyed) + } + + @Test + fun WHEN_configuration_changed_and_discardSavedState_is_true_on_restore_THEN_discards_saved_state() { + var owner = TestOwner() + var ctx = owner.retainedComponent() + ctx.stateKeeper.register(key = "key") { "saved_state" } + + owner = owner.recreate(isChangingConfigurations = true) + ctx = owner.retainedComponent(discardSavedState = true) + val restoredState = ctx.stateKeeper.consume(key = "key") + + assertNull(restoredState) + } + + @Test + fun WHEN_configuration_changed_and_discardSavedState_is_true_on_restore_THEN_instances_not_retained() { + var owner = TestOwner() + var ctx = owner.retainedComponent() + val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + owner = owner.recreate(isChangingConfigurations = true) + ctx = owner.retainedComponent(discardSavedState = true) + val instance2 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + assertNotSame(instance1, instance2) + } + + @Test + fun WHEN_configuration_changed_and_discardSavedState_is_true_on_restore_THEN_old_instances_destroyed() { + var owner = TestOwner() + val ctx = owner.retainedComponent() + val instance1 = ctx.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance) + + owner = owner.recreate(isChangingConfigurations = true) + owner.retainedComponent(discardSavedState = true) + + assertTrue(instance1.isDestroyed) + } + + @Test + fun GIVEN_enabled_BackCallback_registered_WHEN_onBackPressed_THEN_callback_called() { + val owner = TestOwner() + val ctx = owner.retainedComponent() + var isCalled = false + ctx.backHandler.register(BackCallback { isCalled = true }) + + owner.onBackPressedDispatcher.onBackPressed() + + assertTrue(isCalled) + } + + @Test + fun GIVEN_disabled_BackCallback_registered_WHEN_onBackPressed_THEN_callback_not_called() { + val owner = TestOwner() + val ctx = owner.defaultComponentContext() + var isCalled = false + ctx.backHandler.register(BackCallback(isEnabled = false) { isCalled = true }) + + owner.onBackPressedDispatcher.onBackPressed() + + assertFalse(isCalled) + } + + private fun TestOwner.retainedComponent( + discardSavedState: Boolean = false, + isStateSavingAllowed: () -> Boolean = { true }, + isChangingConfigurations: () -> Boolean = { false }, + ): ComponentContext = + retainedComponent( + key = "key", + onBackPressedDispatcher = onBackPressedDispatcher, + discardSavedState = discardSavedState, + isStateSavingAllowed = isStateSavingAllowed, + isChangingConfigurations = isChangingConfigurations, + factory = { it }, + ) +} diff --git a/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/TestOwner.kt b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/TestOwner.kt new file mode 100644 index 000000000..2673ed347 --- /dev/null +++ b/decompose/src/androidUnitTest/kotlin/com/arkivanov/decompose/TestOwner.kt @@ -0,0 +1,57 @@ +package com.arkivanov.decompose + +import android.os.Bundle +import android.os.Parcel +import androidx.activity.OnBackPressedDispatcher +import androidx.activity.OnBackPressedDispatcherOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import androidx.lifecycle.ViewModelStore +import androidx.lifecycle.ViewModelStoreOwner +import androidx.savedstate.SavedStateRegistry +import androidx.savedstate.SavedStateRegistryController +import androidx.savedstate.SavedStateRegistryOwner + +class TestOwner( + savedState: Bundle = Bundle(), + override val viewModelStore: ViewModelStore = ViewModelStore(), +) : LifecycleOwner, SavedStateRegistryOwner, ViewModelStoreOwner, OnBackPressedDispatcherOwner { + private val savedStateRegistryController: SavedStateRegistryController = SavedStateRegistryController.create(this) + override val lifecycle: LifecycleRegistry = LifecycleRegistry(this) + override val savedStateRegistry: SavedStateRegistry get() = savedStateRegistryController.savedStateRegistry + override val onBackPressedDispatcher: OnBackPressedDispatcher = OnBackPressedDispatcher() + + init { + savedStateRegistryController.performRestore(savedState) + lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME) + } + + fun recreate(isChangingConfigurations: Boolean = true): TestOwner { + val bundle = Bundle() + savedStateRegistryController.performSave(bundle) + val savedState = bundle.parcelize().deparcelize() + lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) + + return if (isChangingConfigurations) { + TestOwner(savedState = savedState, viewModelStore = viewModelStore) + } else { + viewModelStore.clear() + TestOwner(savedState = savedState) + } + } + + private fun Bundle.parcelize(): ByteArray { + val parcel = Parcel.obtain() + parcel.writeBundle(this) + return parcel.marshall() + } + + private fun ByteArray.deparcelize(): Bundle { + val parcel = Parcel.obtain() + parcel.unmarshall(this, 0, size) + parcel.setDataPosition(0) + + return requireNotNull(parcel.readBundle()) + } +} From ba70e04db0c2a414e8796009623ea2d37a70a013 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Mon, 22 Jan 2024 21:22:10 +0100 Subject: [PATCH 52/89] Updated Essenty to 2.0.0-alpha03 --- decompose/build.gradle.kts | 1 + .../kotlin/com/arkivanov/decompose/TestUtils.kt | 12 ++++++++++++ .../router/pages/ChildPagesSavedStateTest.kt | 9 +++++---- .../statekeeper/TestStateKeeperDispatcher.kt | 7 ++++--- deps.versions.toml | 2 +- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/decompose/build.gradle.kts b/decompose/build.gradle.kts index 0081171e5..1b8fb165d 100644 --- a/decompose/build.gradle.kts +++ b/decompose/build.gradle.kts @@ -57,6 +57,7 @@ kotlin { common.test.dependencies { implementation(deps.jetbrains.kotlinx.kotlinxCoroutinesCore) + implementation(deps.jetbrains.kotlinx.kotlinxSerializationJson) // Workaround: https://github.com/Kotlin/kotlinx.coroutines/issues/3968 implementation("org.jetbrains.kotlinx:atomicfu:0.23.1") diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/TestUtils.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/TestUtils.kt index 9239ed927..be50466c7 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/TestUtils.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/TestUtils.kt @@ -1,11 +1,23 @@ package com.arkivanov.decompose import com.arkivanov.essenty.statekeeper.StateKeeper +import kotlinx.serialization.json.Json import kotlinx.serialization.serializer +internal val json = + Json { + allowStructuredMapKeys = true + } + inline fun StateKeeper.register(key: String, noinline supplier: () -> T?) { register(key = key, strategy = serializer(), supplier = supplier) } inline fun StateKeeper.consume(key: String): T? = consume(key = key, strategy = serializer()) + +internal inline fun T.serializeAndDeserialize(): T { + val serializer = serializer() + + return json.decodeFromString(serializer, json.encodeToString(serializer, this)) +} diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSavedStateTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSavedStateTest.kt index 36484665f..42899194b 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSavedStateTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/pages/ChildPagesSavedStateTest.kt @@ -1,6 +1,7 @@ package com.arkivanov.decompose.router.pages import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher import com.arkivanov.decompose.value.getValue import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.resume @@ -20,7 +21,7 @@ class ChildPagesSavedStateTest : BaseChildPagesTest() { @Test fun GIVEN_persistent_WHEN_recreated_THEN_state_restored() { - var stateKeeper = StateKeeperDispatcher() + var stateKeeper = TestStateKeeperDispatcher() var context = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = stateKeeper) context.childPages( initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 2), @@ -28,7 +29,7 @@ class ChildPagesSavedStateTest : BaseChildPagesTest() { ) val savedState = stateKeeper.save() - stateKeeper = StateKeeperDispatcher(savedState = savedState) + stateKeeper = TestStateKeeperDispatcher(savedState = savedState) context = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = stateKeeper) val pages2 by context.childPages() @@ -37,7 +38,7 @@ class ChildPagesSavedStateTest : BaseChildPagesTest() { @Test fun GIVEN_not_persistent_WHEN_recreated_THEN_initial_state_applied() { - var stateKeeper = StateKeeperDispatcher() + var stateKeeper = TestStateKeeperDispatcher() var context = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = stateKeeper) context.childPages( initialPages = Pages(items = listOf(0, 1, 2, 3, 4), selectedIndex = 2), @@ -45,7 +46,7 @@ class ChildPagesSavedStateTest : BaseChildPagesTest() { ) val savedState = stateKeeper.save() - stateKeeper = StateKeeperDispatcher(savedState = savedState) + stateKeeper = TestStateKeeperDispatcher(savedState = savedState) context = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = stateKeeper) val pages2 by context.childPages(initialPages = Pages(items = listOf(5, 6, 7), selectedIndex = 0)) diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestStateKeeperDispatcher.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestStateKeeperDispatcher.kt index 63cf1e5a9..8ec6cf81e 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestStateKeeperDispatcher.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/statekeeper/TestStateKeeperDispatcher.kt @@ -1,20 +1,21 @@ package com.arkivanov.decompose.statekeeper +import com.arkivanov.decompose.serializeAndDeserialize import com.arkivanov.essenty.statekeeper.SerializableContainer import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher import kotlinx.serialization.SerializationStrategy import kotlin.test.assertTrue class TestStateKeeperDispatcher( - private val initialSavedState: SerializableContainer? = null, - private val delegate: StateKeeperDispatcher = StateKeeperDispatcher(initialSavedState), + private val savedState: SerializableContainer? = null, + private val delegate: StateKeeperDispatcher = StateKeeperDispatcher(savedState), ) : StateKeeperDispatcher by delegate { var lastSavedState: SerializableContainer? = null private val registeredKeys = HashSet() override fun save(): SerializableContainer = - delegate.save().also { + delegate.save().serializeAndDeserialize().also { lastSavedState = it } diff --git a/deps.versions.toml b/deps.versions.toml index 14f75c813..944ffa3e4 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -2,7 +2,7 @@ decompose = "3.0.0-alpha04" kotlin = "1.9.21" -essenty = "2.0.0-alpha02" +essenty = "2.0.0-alpha03" reaktive = "1.2.3" junit = "4.13.2" jetbrainsCompose = "1.6.0-alpha01" From a9913f703d8421e724f4bb75c14dbc9a80bc48ff Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Mon, 22 Jan 2024 23:28:12 +0100 Subject: [PATCH 53/89] Use Cancellation in NavigationSource and Relay --- decompose/api/android/decompose.api | 6 ++---- decompose/api/jvm/decompose.api | 6 ++---- .../commonMain/kotlin/com/arkivanov/decompose/Relay.kt | 8 ++++---- .../decompose/router/children/ChildrenFactory.kt | 8 +++----- .../decompose/router/children/NavigationSource.kt | 6 +++--- .../decompose/router/children/SimpleNavigation.kt | 8 ++------ .../decompose/router/pages/DefaultPagesNavigation.kt | 8 ++------ .../decompose/router/slot/DefaultSlotNavigation.kt | 8 ++------ .../decompose/router/stack/DefaultStackNavigation.kt | 8 ++------ .../kotlin/com/arkivanov/decompose/RelayTest.kt | 3 +-- 10 files changed, 23 insertions(+), 46 deletions(-) diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 54a9c63a3..763701bc7 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -111,8 +111,7 @@ public abstract interface class com/arkivanov/decompose/router/children/NavState } public abstract interface class com/arkivanov/decompose/router/children/NavigationSource { - public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)V - public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V + public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/Cancellation; } public final class com/arkivanov/decompose/router/children/SimpleChildNavState : com/arkivanov/decompose/router/children/ChildNavState { @@ -131,8 +130,7 @@ public final class com/arkivanov/decompose/router/children/SimpleChildNavState : public final class com/arkivanov/decompose/router/children/SimpleNavigation : com/arkivanov/decompose/router/children/NavigationSource { public fun ()V public final fun navigate (Ljava/lang/Object;)V - public fun subscribe (Lkotlin/jvm/functions/Function1;)V - public fun unsubscribe (Lkotlin/jvm/functions/Function1;)V + public fun subscribe (Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/Cancellation; } public final class com/arkivanov/decompose/router/pages/ChildPages { diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api index 0fa02f181..1461b8525 100644 --- a/decompose/api/jvm/decompose.api +++ b/decompose/api/jvm/decompose.api @@ -95,8 +95,7 @@ public abstract interface class com/arkivanov/decompose/router/children/NavState } public abstract interface class com/arkivanov/decompose/router/children/NavigationSource { - public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)V - public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V + public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/Cancellation; } public final class com/arkivanov/decompose/router/children/SimpleChildNavState : com/arkivanov/decompose/router/children/ChildNavState { @@ -115,8 +114,7 @@ public final class com/arkivanov/decompose/router/children/SimpleChildNavState : public final class com/arkivanov/decompose/router/children/SimpleNavigation : com/arkivanov/decompose/router/children/NavigationSource { public fun ()V public final fun navigate (Ljava/lang/Object;)V - public fun subscribe (Lkotlin/jvm/functions/Function1;)V - public fun unsubscribe (Lkotlin/jvm/functions/Function1;)V + public fun subscribe (Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/Cancellation; } public final class com/arkivanov/decompose/router/pages/ChildPages { diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Relay.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Relay.kt index ffd3e5404..dad154d6b 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Relay.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Relay.kt @@ -50,11 +50,11 @@ internal class Relay( } } - fun subscribe(observer: (T) -> Unit) { + fun subscribe(observer: (T) -> Unit): Cancellation { lock.synchronized { observers += observer } - } - fun unsubscribe(observer: (T) -> Unit) { - lock.synchronized { observers -= observer } + return Cancellation { + lock.synchronized { observers -= observer } + } } } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt index f37345e69..fe801d5ef 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt @@ -166,8 +166,8 @@ fun , S : Any> ComponentContext.child mainBackHandler.register(backCallback) - val eventObserver: (E) -> Unit = - { event -> + val cancellation = + source.subscribe { event -> val oldState = navigator.navState navigator.navigate(navState = navTransformer(navigator.navState, event)) val newState = navigator.navState @@ -175,10 +175,8 @@ fun , S : Any> ComponentContext.child onEventComplete(event, newState, oldState) } - source.subscribe(eventObserver) - lifecycle.doOnDestroy { - source.unsubscribe(eventObserver) + cancellation.cancel() stateKeeper.unregister(key = key) mainBackHandler.unregister(backCallback) } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavigationSource.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavigationSource.kt index e29ac4926..539a79c73 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavigationSource.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavigationSource.kt @@ -1,5 +1,7 @@ package com.arkivanov.decompose.router.children +import com.arkivanov.decompose.Cancellation + /** * Represents a generic source of navigation events. * @@ -7,7 +9,5 @@ package com.arkivanov.decompose.router.children */ interface NavigationSource { - fun subscribe(observer: (T) -> Unit) - - fun unsubscribe(observer: (T) -> Unit) + fun subscribe(observer: (T) -> Unit): Cancellation } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleNavigation.kt index de9d56774..6270f2660 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleNavigation.kt @@ -1,5 +1,6 @@ package com.arkivanov.decompose.router.children +import com.arkivanov.decompose.Cancellation import com.arkivanov.decompose.Relay /** @@ -10,13 +11,8 @@ class SimpleNavigation : NavigationSource { private val relay = Relay(isMainThreadCheckEnabled = true) - override fun subscribe(observer: (T) -> Unit) { + override fun subscribe(observer: (T) -> Unit): Cancellation = relay.subscribe(observer) - } - - override fun unsubscribe(observer: (T) -> Unit) { - relay.unsubscribe(observer) - } /** * Broadcasts the navigation event to every subscribed observer. diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/DefaultPagesNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/DefaultPagesNavigation.kt index cbe2d0209..8a57af499 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/DefaultPagesNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/DefaultPagesNavigation.kt @@ -1,5 +1,6 @@ package com.arkivanov.decompose.router.pages +import com.arkivanov.decompose.Cancellation import com.arkivanov.decompose.Relay import com.arkivanov.decompose.router.pages.PagesNavigationSource.Event @@ -7,13 +8,8 @@ internal class DefaultPagesNavigation : PagesNavigation { private val relay = Relay>() - override fun subscribe(observer: (Event) -> Unit) { + override fun subscribe(observer: (Event) -> Unit): Cancellation = relay.subscribe(observer) - } - - override fun unsubscribe(observer: (Event) -> Unit) { - relay.unsubscribe(observer) - } override fun navigate(transformer: (Pages) -> Pages, onComplete: (newPages: Pages, oldPages: Pages) -> Unit) { relay.accept(Event(transformer, onComplete)) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt index b0c2502e3..811825880 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt @@ -1,5 +1,6 @@ package com.arkivanov.decompose.router.slot +import com.arkivanov.decompose.Cancellation import com.arkivanov.decompose.Relay import com.arkivanov.decompose.router.slot.SlotNavigationSource.Event @@ -14,11 +15,6 @@ internal class DefaultSlotNavigation : SlotNavigation { relay.accept(Event(transformer, onComplete)) } - override fun subscribe(observer: (Event) -> Unit) { + override fun subscribe(observer: (Event) -> Unit): Cancellation = relay.subscribe(observer) - } - - override fun unsubscribe(observer: (Event) -> Unit) { - relay.unsubscribe(observer) - } } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt index 8105ff371..d62333b02 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt @@ -1,5 +1,6 @@ package com.arkivanov.decompose.router.stack +import com.arkivanov.decompose.Cancellation import com.arkivanov.decompose.Relay import com.arkivanov.decompose.router.stack.StackNavigationSource.Event @@ -11,11 +12,6 @@ internal class DefaultStackNavigation : StackNavigation { relay.accept(Event(transformer, onComplete)) } - override fun subscribe(observer: (Event) -> Unit) { + override fun subscribe(observer: (Event) -> Unit): Cancellation = relay.subscribe(observer) - } - - override fun unsubscribe(observer: (Event) -> Unit) { - relay.unsubscribe(observer) - } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/RelayTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/RelayTest.kt index 41a9c761d..b4f3cde13 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/RelayTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/RelayTest.kt @@ -38,8 +38,7 @@ class RelayTest { val relay = Relay() var isEmitted = false val observer: (Int) -> Unit = { isEmitted = true } - relay.subscribe(observer) - relay.unsubscribe(observer) + relay.subscribe(observer).cancel() relay.accept(1) From 1994de2fdb97fe2051b198888685718fdcce2f79 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Mon, 22 Jan 2024 23:32:11 +0100 Subject: [PATCH 54/89] Moved PredictiveBackGestureOverlay and PredictiveBackGestureIcon inside predictiveback package --- .../api/jvm/extensions-compose.api | 16 ++++++++-------- .../predictiveback}/PredictiveBackGestureIcon.kt | 2 +- .../PredictiveBackGestureOverlay.kt | 2 +- .../sample/shared/root/RootViewController.kt | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) rename extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/{ => stack/animation/predictiveback}/PredictiveBackGestureIcon.kt (93%) rename extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/{ => stack/animation/predictiveback}/PredictiveBackGestureOverlay.kt (99%) diff --git a/extensions-compose/api/jvm/extensions-compose.api b/extensions-compose/api/jvm/extensions-compose.api index 248ab13cb..50a47c51a 100644 --- a/extensions-compose/api/jvm/extensions-compose.api +++ b/extensions-compose/api/jvm/extensions-compose.api @@ -1,11 +1,3 @@ -public final class com/arkivanov/decompose/extensions/compose/PredictiveBackGestureIconKt { - public static final fun PredictiveBackGestureIcon-eaDK9VM (Landroidx/compose/ui/graphics/vector/ImageVector;FJJLandroidx/compose/runtime/Composer;II)V -} - -public final class com/arkivanov/decompose/extensions/compose/PredictiveBackGestureOverlayKt { - public static final fun PredictiveBackGestureOverlay (Lcom/arkivanov/essenty/backhandler/BackDispatcher;Lkotlin/jvm/functions/Function4;Landroidx/compose/ui/Modifier;ZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V -} - public final class com/arkivanov/decompose/extensions/compose/SubscribeAsStateKt { public static final fun subscribeAsState (Lcom/arkivanov/decompose/value/Value;Landroidx/compose/runtime/SnapshotMutationPolicy;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State; } @@ -144,3 +136,11 @@ public final class com/arkivanov/decompose/extensions/compose/stack/animation/pr public static synthetic fun predictiveBackAnimation$default (Lcom/arkivanov/essenty/backhandler/BackHandler;Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimation; } +public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureIconKt { + public static final fun PredictiveBackGestureIcon-eaDK9VM (Landroidx/compose/ui/graphics/vector/ImageVector;FJJLandroidx/compose/runtime/Composer;II)V +} + +public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlayKt { + public static final fun PredictiveBackGestureOverlay (Lcom/arkivanov/essenty/backhandler/BackDispatcher;Lkotlin/jvm/functions/Function4;Landroidx/compose/ui/Modifier;ZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V +} + diff --git a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureIcon.kt b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureIcon.kt similarity index 93% rename from extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureIcon.kt rename to extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureIcon.kt index e2a75a6a5..ada517baf 100644 --- a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureIcon.kt +++ b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureIcon.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import androidx.compose.foundation.background import androidx.compose.foundation.layout.padding diff --git a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureOverlay.kt b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt similarity index 99% rename from extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureOverlay.kt rename to extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt index 2800ad614..34db76ec0 100644 --- a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/PredictiveBackGestureOverlay.kt +++ b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt @@ -1,4 +1,4 @@ -package com.arkivanov.decompose.extensions.compose +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn diff --git a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt index 4a1f3deff..2909bfaf4 100644 --- a/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt +++ b/sample/shared/compose/src/iosMain/kotlin/com/arkivanov/sample/shared/root/RootViewController.kt @@ -6,8 +6,8 @@ import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.ui.Modifier import androidx.compose.ui.window.ComposeUIViewController import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.decompose.extensions.compose.PredictiveBackGestureIcon -import com.arkivanov.decompose.extensions.compose.PredictiveBackGestureOverlay +import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.PredictiveBackGestureIcon +import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.PredictiveBackGestureOverlay import com.arkivanov.essenty.backhandler.BackDispatcher import platform.UIKit.UIViewController From 298b052ff4577ea8217d86084f1b9b11beb61470 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Tue, 23 Jan 2024 16:52:14 +0000 Subject: [PATCH 55/89] Bumped version to 3.0.0-alpha05 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 944ffa3e4..f0282424d 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "3.0.0-alpha04" +decompose = "3.0.0-alpha05" kotlin = "1.9.21" essenty = "2.0.0-alpha03" reaktive = "1.2.3" From cc7e1344b442ff10f5cd7068e1306318580c45ec Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 27 Jan 2024 11:54:35 +0000 Subject: [PATCH 56/89] Animate predictive back gesture cancellation --- extensions-compose/api/android/extensions-compose.api | 1 + extensions-compose/api/jvm/extensions-compose.api | 1 + .../predictiveback/DefaultPredictiveBackAnimatable.kt | 4 ++++ .../predictiveback/MaterialPredictiveBackAnimatable.kt | 4 ++++ .../animation/predictiveback/PredictiveBackAnimatable.kt | 8 ++++++++ .../animation/predictiveback/PredictiveBackAnimation.kt | 9 +++++++-- 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/extensions-compose/api/android/extensions-compose.api b/extensions-compose/api/android/extensions-compose.api index 4894346d2..0924f0299 100644 --- a/extensions-compose/api/android/extensions-compose.api +++ b/extensions-compose/api/android/extensions-compose.api @@ -118,6 +118,7 @@ public final class com/arkivanov/decompose/extensions/compose/stack/animation/pr public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable { public abstract fun animate (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun cancel (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun finish (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun getEnterModifier ()Landroidx/compose/ui/Modifier; public abstract fun getExitModifier ()Landroidx/compose/ui/Modifier; diff --git a/extensions-compose/api/jvm/extensions-compose.api b/extensions-compose/api/jvm/extensions-compose.api index 50a47c51a..e3751c948 100644 --- a/extensions-compose/api/jvm/extensions-compose.api +++ b/extensions-compose/api/jvm/extensions-compose.api @@ -122,6 +122,7 @@ public final class com/arkivanov/decompose/extensions/compose/stack/animation/pr public abstract interface class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable { public abstract fun animate (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun cancel (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun finish (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun getEnterModifier ()Landroidx/compose/ui/Modifier; public abstract fun getExitModifier ()Landroidx/compose/ui/Modifier; diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt index 0bd551520..e0e8126da 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/DefaultPredictiveBackAnimatable.kt @@ -28,4 +28,8 @@ internal class DefaultPredictiveBackAnimatable( override suspend fun finish() { progressAnimatable.animateTo(targetValue = 1F) } + + override suspend fun cancel() { + progressAnimatable.animateTo(targetValue = 0F) + } } diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt index 3fb1ad834..20675c2af 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt @@ -131,4 +131,8 @@ private class MaterialPredictiveBackAnimatable( ) } } + + override suspend fun cancel() { + progressAnimatable.animateTo(targetValue = 0F) + } } diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable.kt index 4e7da8a8d..78da020ce 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable.kt @@ -39,6 +39,14 @@ interface PredictiveBackAnimatable { * @see androidx.compose.animation.core.Animatable */ suspend fun finish() + + /** + * Animates both [exitModifier] and [enterModifier] towards the initial state. + * Any previous animation must be cancelled. + * + * @see androidx.compose.animation.core.Animatable + */ + suspend fun cancel() } /** diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt index e9a4fe854..610b8475d 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt @@ -126,7 +126,10 @@ private class PredictiveBackAnimation( scope.launch { data.animatable?.animate(it) } }, onBackCancelled = { - data = data.copy(animatable = null) + scope.launch { + data.animatable?.cancel() + data = data.copy(animatable = null) + } }, onBack = { if (data.animatable == null) { @@ -134,7 +137,9 @@ private class PredictiveBackAnimation( } else { scope.launch { data.animatable?.finish() - onBack() + if (data.animatable != null) { + onBack() + } } } } From 3813657fc162bf0eeb8ea207b9723ec7a27d92af Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 27 Jan 2024 17:01:20 +0000 Subject: [PATCH 57/89] Added edgeWidth, activationOffsetThreshold and confirmationProgressThreshold parameters for PredictiveBackGestureOverlay --- .../api/jvm/extensions-compose.api | 2 +- .../PredictiveBackGestureOverlay.kt | 45 +++++++++++++------ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/extensions-compose/api/jvm/extensions-compose.api b/extensions-compose/api/jvm/extensions-compose.api index e3751c948..2c57ea641 100644 --- a/extensions-compose/api/jvm/extensions-compose.api +++ b/extensions-compose/api/jvm/extensions-compose.api @@ -142,6 +142,6 @@ public final class com/arkivanov/decompose/extensions/compose/stack/animation/pr } public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlayKt { - public static final fun PredictiveBackGestureOverlay (Lcom/arkivanov/essenty/backhandler/BackDispatcher;Lkotlin/jvm/functions/Function4;Landroidx/compose/ui/Modifier;ZZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun PredictiveBackGestureOverlay-_UtchM0 (Lcom/arkivanov/essenty/backhandler/BackDispatcher;Lkotlin/jvm/functions/Function4;Landroidx/compose/ui/Modifier;ZZFFFLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V } diff --git a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt index 34db76ec0..efb3bebad 100644 --- a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt +++ b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp @@ -41,7 +42,13 @@ import com.arkivanov.essenty.backhandler.BackEvent.SwipeEdge * left in RTL mode and right in LTR mode. * @param endEdgeEnabled controls whether the end edge is enabled or not, * right in RTL mode and left in LTR mode. - * @param onClose If supplied, then the back gesture is also handled when there are no other enabled back + * @param edgeWidth the width in [Dp] from the screen edge where the gesture first down touch is recognized. + * @param activationOffsetThreshold a distance threshold in [Dp] from the initial touch point in the direction + * of gesture. The gesture is initiated once this threshold is surpassed. + * @param confirmationProgressThreshold a threshold of progress that needs to be reached for the gesture + * to be confirmed once the touch is completed. The gesture is cancelled if the touch is completed without + * reaching the threshold. + * @param onClose if supplied, then the back gesture is also handled when there are no other enabled back * callbacks registered in [backDispatcher], can be used to close the application. * @param content a content to be shown under the overlay. */ @@ -53,6 +60,9 @@ fun PredictiveBackGestureOverlay( modifier: Modifier = Modifier, startEdgeEnabled: Boolean = true, endEdgeEnabled: Boolean = true, + edgeWidth: Dp = 16.dp, + activationOffsetThreshold: Dp = 16.dp, + confirmationProgressThreshold: Float = 0.2F, onClose: (() -> Unit)? = null, content: @Composable () -> Unit, ) { @@ -70,6 +80,9 @@ fun PredictiveBackGestureOverlay( LayoutDirection.Ltr -> endEdgeEnabled LayoutDirection.Rtl -> startEdgeEnabled }, + edgeWidth = edgeWidth, + activationOffsetThreshold = activationOffsetThreshold, + confirmationProgressThreshold = confirmationProgressThreshold, onIconMoved = { position, progress, edge -> iconState.value = IconState( @@ -110,6 +123,9 @@ private fun Modifier.handleBackGestures( backDispatcher: BackDispatcher, leftEdgeEnabled: Boolean, rightEdgeEnabled: Boolean, + edgeWidth: Dp, + activationOffsetThreshold: Dp, + confirmationProgressThreshold: Float, onIconMoved: (position: Offset, progress: Float, BackGestureHandler.Edge) -> Unit, onIconHidden: () -> Unit, ): Modifier = @@ -120,8 +136,8 @@ private fun Modifier.handleBackGestures( val down = awaitFirstDown(pass = PointerEventPass.Initial) val startPosition = down.position - val isLeftInvalid = !leftEdgeEnabled || (startPosition.x > 16.dp.toPx()) - val isRightInvalid = !rightEdgeEnabled || (startPosition.x < size.width - 16.dp.toPx()) + val isLeftInvalid = !leftEdgeEnabled || (startPosition.x > edgeWidth.toPx()) + val isRightInvalid = !rightEdgeEnabled || (startPosition.x < size.width - edgeWidth.toPx()) if (isLeftInvalid && isRightInvalid) { return@awaitEachGesture @@ -131,8 +147,9 @@ private fun Modifier.handleBackGestures( BackGestureHandler( startPosition = startPosition, size = size, - cancelYOffset = 16.dp.toPx(), - startXOffset = 16.dp.toPx(), + offsetIgnoreThreshold = 16.dp.toPx(), + activationOffsetThreshold = activationOffsetThreshold.toPx(), + progressConfirmationThreshold = confirmationProgressThreshold, backDispatcher = backDispatcher, onIconMoved = onIconMoved, ) @@ -168,8 +185,9 @@ private data class IconState( private class BackGestureHandler( private val startPosition: Offset, private val size: IntSize, - private val cancelYOffset: Float, - private val startXOffset: Float, + private val offsetIgnoreThreshold: Float, + private val activationOffsetThreshold: Float, + private val progressConfirmationThreshold: Float, private val backDispatcher: BackDispatcher, private val onIconMoved: (position: Offset, progress: Float, Edge) -> Unit, ) { @@ -198,18 +216,18 @@ private class BackGestureHandler( val change = awaitChange() val position = change.position - if ((position.y < startPosition.y - cancelYOffset) || (position.y > startPosition.y + cancelYOffset)) { + if ((position.y < startPosition.y - offsetIgnoreThreshold) || (position.y > startPosition.y + offsetIgnoreThreshold)) { return null } when (edge) { Edge.LEFT -> - if (position.x > startPosition.x + startXOffset) { + if (position.x > startPosition.x + activationOffsetThreshold) { return change } Edge.RIGHT -> - if (position.x < startPosition.x - startXOffset) { + if (position.x < startPosition.x - activationOffsetThreshold) { return change } } @@ -252,8 +270,9 @@ private class BackGestureHandler( ) ) + println("Progress: $progress") if (!change.pressed) { - if (progress > 0.2F) { + if (progress > progressConfirmationThreshold) { dispatcher.finish() } else { dispatcher.cancel() @@ -269,12 +288,12 @@ private class BackGestureHandler( private fun getProgress(position: Offset): Float = when (edge) { Edge.LEFT -> { - val startX = startPosition.x + startXOffset + val startX = startPosition.x + activationOffsetThreshold (position.x - startX) / (size.width - startX) } Edge.RIGHT -> { - val startX = startPosition.x - startXOffset + val startX = startPosition.x - activationOffsetThreshold (startX - position.x) / startX } }.coerceIn(0F, 1F) From 9c5312bbb88c7dbc59344e28f31f0f4ad209ffe4 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 1 Feb 2024 20:14:06 +0000 Subject: [PATCH 58/89] Removed StackNavigationSource, SlotNavigationSource and PagesNavigationSource interfaces --- decompose/api/android/decompose.api | 73 ++++++++----------- decompose/api/jvm/decompose.api | 73 ++++++++----------- .../router/pages/ChildPagesFactory.kt | 5 +- .../router/pages/DefaultPagesNavigation.kt | 2 +- .../decompose/router/pages/PagesNavigation.kt | 10 ++- .../router/pages/PagesNavigationSource.kt | 18 ----- .../decompose/router/slot/ChildSlotFactory.kt | 5 +- .../router/slot/DefaultSlotNavigation.kt | 2 +- .../decompose/router/slot/SlotNavigation.kt | 11 ++- .../router/slot/SlotNavigationSource.kt | 16 ---- .../router/stack/ChildStackFactory.kt | 7 +- .../router/stack/DefaultStackNavigation.kt | 2 +- .../decompose/router/stack/StackNavigation.kt | 13 +++- .../router/stack/StackNavigationSource.kt | 16 ---- 14 files changed, 107 insertions(+), 146 deletions(-) delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/PagesNavigationSource.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigationSource.kt delete mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigationSource.kt diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 763701bc7..3db91a65e 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -148,10 +148,10 @@ public final class com/arkivanov/decompose/router/pages/ChildPages { } public final class com/arkivanov/decompose/router/pages/ChildPagesFactoryKt { - public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static final fun getDefaultPageStatus (ILcom/arkivanov/decompose/router/pages/Pages;)Lcom/arkivanov/decompose/router/children/ChildNavState$Status; } @@ -169,23 +169,20 @@ public final class com/arkivanov/decompose/router/pages/Pages { public fun toString ()Ljava/lang/String; } -public abstract interface class com/arkivanov/decompose/router/pages/PagesNavigation : com/arkivanov/decompose/router/pages/PagesNavigationSource, com/arkivanov/decompose/router/pages/PagesNavigator { +public abstract interface class com/arkivanov/decompose/router/pages/PagesNavigation : com/arkivanov/decompose/router/children/NavigationSource, com/arkivanov/decompose/router/pages/PagesNavigator { } -public final class com/arkivanov/decompose/router/pages/PagesNavigationKt { - public static final fun PagesNavigation ()Lcom/arkivanov/decompose/router/pages/PagesNavigation; -} - -public abstract interface class com/arkivanov/decompose/router/pages/PagesNavigationSource : com/arkivanov/decompose/router/children/NavigationSource { -} - -public final class com/arkivanov/decompose/router/pages/PagesNavigationSource$Event { +public final class com/arkivanov/decompose/router/pages/PagesNavigation$Event { public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getOnComplete ()Lkotlin/jvm/functions/Function2; public final fun getTransformer ()Lkotlin/jvm/functions/Function1; } +public final class com/arkivanov/decompose/router/pages/PagesNavigationKt { + public static final fun PagesNavigation ()Lcom/arkivanov/decompose/router/pages/PagesNavigation; +} + public abstract interface class com/arkivanov/decompose/router/pages/PagesNavigator { public abstract fun navigate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V } @@ -220,29 +217,26 @@ public final class com/arkivanov/decompose/router/slot/ChildSlot { } public final class com/arkivanov/decompose/router/slot/ChildSlotFactoryKt { - public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; -} - -public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigation : com/arkivanov/decompose/router/slot/SlotNavigationSource, com/arkivanov/decompose/router/slot/SlotNavigator { -} - -public final class com/arkivanov/decompose/router/slot/SlotNavigationKt { - public static final fun SlotNavigation ()Lcom/arkivanov/decompose/router/slot/SlotNavigation; + public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } -public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigationSource : com/arkivanov/decompose/router/children/NavigationSource { +public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigation : com/arkivanov/decompose/router/children/NavigationSource, com/arkivanov/decompose/router/slot/SlotNavigator { } -public final class com/arkivanov/decompose/router/slot/SlotNavigationSource$Event { +public final class com/arkivanov/decompose/router/slot/SlotNavigation$Event { public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getOnComplete ()Lkotlin/jvm/functions/Function2; public final fun getTransformer ()Lkotlin/jvm/functions/Function1; } +public final class com/arkivanov/decompose/router/slot/SlotNavigationKt { + public static final fun SlotNavigation ()Lcom/arkivanov/decompose/router/slot/SlotNavigation; +} + public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigator { public abstract fun navigate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V } @@ -276,31 +270,28 @@ public final class com/arkivanov/decompose/router/stack/ChildStack { } public final class com/arkivanov/decompose/router/stack/ChildStackFactoryKt { - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } -public abstract interface class com/arkivanov/decompose/router/stack/StackNavigation : com/arkivanov/decompose/router/stack/StackNavigationSource, com/arkivanov/decompose/router/stack/StackNavigator { +public abstract interface class com/arkivanov/decompose/router/stack/StackNavigation : com/arkivanov/decompose/router/children/NavigationSource, com/arkivanov/decompose/router/stack/StackNavigator { } -public final class com/arkivanov/decompose/router/stack/StackNavigationKt { - public static final fun StackNavigation ()Lcom/arkivanov/decompose/router/stack/StackNavigation; -} - -public abstract interface class com/arkivanov/decompose/router/stack/StackNavigationSource : com/arkivanov/decompose/router/children/NavigationSource { -} - -public final class com/arkivanov/decompose/router/stack/StackNavigationSource$Event { +public final class com/arkivanov/decompose/router/stack/StackNavigation$Event { public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getOnComplete ()Lkotlin/jvm/functions/Function2; public final fun getTransformer ()Lkotlin/jvm/functions/Function1; } +public final class com/arkivanov/decompose/router/stack/StackNavigationKt { + public static final fun StackNavigation ()Lcom/arkivanov/decompose/router/stack/StackNavigation; +} + public abstract interface class com/arkivanov/decompose/router/stack/StackNavigator { public abstract fun navigate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V } diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api index 1461b8525..f77831103 100644 --- a/decompose/api/jvm/decompose.api +++ b/decompose/api/jvm/decompose.api @@ -132,10 +132,10 @@ public final class com/arkivanov/decompose/router/pages/ChildPages { } public final class com/arkivanov/decompose/router/pages/ChildPagesFactoryKt { - public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/pages/PagesNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static final fun getDefaultPageStatus (ILcom/arkivanov/decompose/router/pages/Pages;)Lcom/arkivanov/decompose/router/children/ChildNavState$Status; } @@ -153,23 +153,20 @@ public final class com/arkivanov/decompose/router/pages/Pages { public fun toString ()Ljava/lang/String; } -public abstract interface class com/arkivanov/decompose/router/pages/PagesNavigation : com/arkivanov/decompose/router/pages/PagesNavigationSource, com/arkivanov/decompose/router/pages/PagesNavigator { +public abstract interface class com/arkivanov/decompose/router/pages/PagesNavigation : com/arkivanov/decompose/router/children/NavigationSource, com/arkivanov/decompose/router/pages/PagesNavigator { } -public final class com/arkivanov/decompose/router/pages/PagesNavigationKt { - public static final fun PagesNavigation ()Lcom/arkivanov/decompose/router/pages/PagesNavigation; -} - -public abstract interface class com/arkivanov/decompose/router/pages/PagesNavigationSource : com/arkivanov/decompose/router/children/NavigationSource { -} - -public final class com/arkivanov/decompose/router/pages/PagesNavigationSource$Event { +public final class com/arkivanov/decompose/router/pages/PagesNavigation$Event { public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getOnComplete ()Lkotlin/jvm/functions/Function2; public final fun getTransformer ()Lkotlin/jvm/functions/Function1; } +public final class com/arkivanov/decompose/router/pages/PagesNavigationKt { + public static final fun PagesNavigation ()Lcom/arkivanov/decompose/router/pages/PagesNavigation; +} + public abstract interface class com/arkivanov/decompose/router/pages/PagesNavigator { public abstract fun navigate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V } @@ -204,29 +201,26 @@ public final class com/arkivanov/decompose/router/slot/ChildSlot { } public final class com/arkivanov/decompose/router/slot/ChildSlotFactoryKt { - public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/slot/SlotNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; -} - -public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigation : com/arkivanov/decompose/router/slot/SlotNavigationSource, com/arkivanov/decompose/router/slot/SlotNavigator { -} - -public final class com/arkivanov/decompose/router/slot/SlotNavigationKt { - public static final fun SlotNavigation ()Lcom/arkivanov/decompose/router/slot/SlotNavigation; + public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } -public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigationSource : com/arkivanov/decompose/router/children/NavigationSource { +public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigation : com/arkivanov/decompose/router/children/NavigationSource, com/arkivanov/decompose/router/slot/SlotNavigator { } -public final class com/arkivanov/decompose/router/slot/SlotNavigationSource$Event { +public final class com/arkivanov/decompose/router/slot/SlotNavigation$Event { public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getOnComplete ()Lkotlin/jvm/functions/Function2; public final fun getTransformer ()Lkotlin/jvm/functions/Function1; } +public final class com/arkivanov/decompose/router/slot/SlotNavigationKt { + public static final fun SlotNavigation ()Lcom/arkivanov/decompose/router/slot/SlotNavigation; +} + public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigator { public abstract fun navigate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V } @@ -260,31 +254,28 @@ public final class com/arkivanov/decompose/router/stack/ChildStack { } public final class com/arkivanov/decompose/router/stack/ChildStackFactoryKt { - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } -public abstract interface class com/arkivanov/decompose/router/stack/StackNavigation : com/arkivanov/decompose/router/stack/StackNavigationSource, com/arkivanov/decompose/router/stack/StackNavigator { +public abstract interface class com/arkivanov/decompose/router/stack/StackNavigation : com/arkivanov/decompose/router/children/NavigationSource, com/arkivanov/decompose/router/stack/StackNavigator { } -public final class com/arkivanov/decompose/router/stack/StackNavigationKt { - public static final fun StackNavigation ()Lcom/arkivanov/decompose/router/stack/StackNavigation; -} - -public abstract interface class com/arkivanov/decompose/router/stack/StackNavigationSource : com/arkivanov/decompose/router/children/NavigationSource { -} - -public final class com/arkivanov/decompose/router/stack/StackNavigationSource$Event { +public final class com/arkivanov/decompose/router/stack/StackNavigation$Event { public fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getOnComplete ()Lkotlin/jvm/functions/Function2; public final fun getTransformer ()Lkotlin/jvm/functions/Function1; } +public final class com/arkivanov/decompose/router/stack/StackNavigationKt { + public static final fun StackNavigation ()Lcom/arkivanov/decompose/router/stack/StackNavigation; +} + public abstract interface class com/arkivanov/decompose/router/stack/StackNavigator { public abstract fun navigate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V } diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt index cf7a437fb..0aa9b1675 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt @@ -5,6 +5,7 @@ import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.router.children.ChildNavState import com.arkivanov.decompose.router.children.ChildNavState.Status import com.arkivanov.decompose.router.children.NavState +import com.arkivanov.decompose.router.children.NavigationSource import com.arkivanov.decompose.router.children.SimpleChildNavState import com.arkivanov.decompose.router.children.children import com.arkivanov.decompose.value.Value @@ -35,7 +36,7 @@ import kotlinx.serialization.Serializable */ @ExperimentalDecomposeApi fun ComponentContext.childPages( - source: PagesNavigationSource, + source: NavigationSource>, serializer: KSerializer?, initialPages: () -> Pages = { Pages() }, key: String = "DefaultChildPages", @@ -104,7 +105,7 @@ private class SerializablePages( */ @ExperimentalDecomposeApi fun ComponentContext.childPages( - source: PagesNavigationSource, + source: NavigationSource>, initialPages: () -> Pages, savePages: (Pages) -> SerializableContainer?, restorePages: (SerializableContainer) -> Pages?, diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/DefaultPagesNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/DefaultPagesNavigation.kt index 8a57af499..053afe67d 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/DefaultPagesNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/DefaultPagesNavigation.kt @@ -2,7 +2,7 @@ package com.arkivanov.decompose.router.pages import com.arkivanov.decompose.Cancellation import com.arkivanov.decompose.Relay -import com.arkivanov.decompose.router.pages.PagesNavigationSource.Event +import com.arkivanov.decompose.router.pages.PagesNavigation.Event internal class DefaultPagesNavigation : PagesNavigation { diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/PagesNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/PagesNavigation.kt index 13fbc6ad2..56a62d73f 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/PagesNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/PagesNavigation.kt @@ -1,12 +1,20 @@ package com.arkivanov.decompose.router.pages import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.decompose.router.children.NavigationSource +import com.arkivanov.decompose.router.pages.PagesNavigation.Event /** * Represents [PagesNavigator] and [PagesNavigationSource] at the same time. */ @ExperimentalDecomposeApi -interface PagesNavigation : PagesNavigator, PagesNavigationSource +interface PagesNavigation : PagesNavigator, NavigationSource> { + + class Event( + val transformer: (Pages) -> Pages, + val onComplete: (newPages: Pages, oldPages: Pages) -> Unit = { _, _ -> }, + ) +} /** * Returns a default implementation of [PagesNavigation]. diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/PagesNavigationSource.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/PagesNavigationSource.kt deleted file mode 100644 index 10b6513c5..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/PagesNavigationSource.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.arkivanov.decompose.router.pages - -import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.decompose.router.children.NavigationSource - -/** - * Represents a source of navigation events for Child Pages. - * - * @see PagesNavigator - */ -@ExperimentalDecomposeApi -interface PagesNavigationSource : NavigationSource> { - - class Event( - val transformer: (Pages) -> Pages, - val onComplete: (newPages: Pages, oldPages: Pages) -> Unit = { _, _ -> }, - ) -} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt index 926a511f5..5e8564359 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt @@ -4,6 +4,7 @@ import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.children.ChildNavState.Status import com.arkivanov.decompose.router.children.NavState +import com.arkivanov.decompose.router.children.NavigationSource import com.arkivanov.decompose.router.children.SimpleChildNavState import com.arkivanov.decompose.router.children.children import com.arkivanov.decompose.value.Value @@ -27,7 +28,7 @@ import kotlinx.serialization.KSerializer * @return an observable [Value] of [ChildSlot]. */ fun ComponentContext.childSlot( - source: SlotNavigationSource, + source: NavigationSource>, serializer: KSerializer?, initialConfiguration: () -> C? = { null }, key: String = "DefaultChildSlot", @@ -72,7 +73,7 @@ fun ComponentContext.childSlot( * @return an observable [Value] of [ChildSlot]. */ fun ComponentContext.childSlot( - source: SlotNavigationSource, + source: NavigationSource>, saveConfiguration: (C?) -> SerializableContainer?, restoreConfiguration: (SerializableContainer) -> C?, key: String = "DefaultChildSlot", diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt index 811825880..599217272 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt @@ -2,7 +2,7 @@ package com.arkivanov.decompose.router.slot import com.arkivanov.decompose.Cancellation import com.arkivanov.decompose.Relay -import com.arkivanov.decompose.router.slot.SlotNavigationSource.Event +import com.arkivanov.decompose.router.slot.SlotNavigation.Event internal class DefaultSlotNavigation : SlotNavigation { diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigation.kt index f250bf64f..bdf3b37be 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigation.kt @@ -1,9 +1,18 @@ package com.arkivanov.decompose.router.slot +import com.arkivanov.decompose.router.children.NavigationSource +import com.arkivanov.decompose.router.slot.SlotNavigation.Event + /** * Represents [SlotNavigator] and [SlotNavigationSource] at the same time. */ -interface SlotNavigation : SlotNavigator, SlotNavigationSource +interface SlotNavigation : SlotNavigator, NavigationSource> { + + class Event( + val transformer: (configuration: C?) -> C?, + val onComplete: (newConfiguration: C?, oldConfiguration: C?) -> Unit = { _, _ -> }, + ) +} /** * Returns a default implementation of [SlotNavigation]. diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigationSource.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigationSource.kt deleted file mode 100644 index 3d12780c4..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigationSource.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.arkivanov.decompose.router.slot - -import com.arkivanov.decompose.router.children.NavigationSource - -/** - * Represents a source of navigation events for `Child Slot`. - * - * @see SlotNavigator - */ -interface SlotNavigationSource : NavigationSource> { - - class Event( - val transformer: (configuration: C?) -> C?, - val onComplete: (newConfiguration: C?, oldConfiguration: C?) -> Unit = { _, _ -> }, - ) -} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt index 4480b09b1..a5a5e50c5 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt @@ -4,6 +4,7 @@ import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.children.ChildNavState.Status import com.arkivanov.decompose.router.children.NavState +import com.arkivanov.decompose.router.children.NavigationSource import com.arkivanov.decompose.router.children.SimpleChildNavState import com.arkivanov.decompose.router.children.children import com.arkivanov.decompose.value.Value @@ -27,7 +28,7 @@ import kotlinx.serialization.builtins.ListSerializer * @return an observable [Value] of [ChildStack]. */ fun ComponentContext.childStack( - source: StackNavigationSource, + source: NavigationSource>, serializer: KSerializer?, initialStack: () -> List, key: String = "DefaultChildStack", @@ -60,7 +61,7 @@ fun ComponentContext.childStack( * A convenience extension function for [ComponentContext.childStack]. */ fun ComponentContext.childStack( - source: StackNavigationSource, + source: NavigationSource>, serializer: KSerializer?, initialConfiguration: C, key: String = "DefaultChildStack", @@ -94,7 +95,7 @@ fun ComponentContext.childStack( * @return an observable [Value] of [ChildStack]. */ fun ComponentContext.childStack( - source: StackNavigationSource, + source: NavigationSource>, initialStack: () -> List, saveStack: (List) -> SerializableContainer?, restoreStack: (SerializableContainer) -> List?, diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt index d62333b02..b1c797584 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt @@ -2,7 +2,7 @@ package com.arkivanov.decompose.router.stack import com.arkivanov.decompose.Cancellation import com.arkivanov.decompose.Relay -import com.arkivanov.decompose.router.stack.StackNavigationSource.Event +import com.arkivanov.decompose.router.stack.StackNavigation.Event internal class DefaultStackNavigation : StackNavigation { diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigation.kt index a969e9775..0b7256af9 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigation.kt @@ -1,9 +1,18 @@ package com.arkivanov.decompose.router.stack +import com.arkivanov.decompose.router.children.NavigationSource +import com.arkivanov.decompose.router.stack.StackNavigation.Event + /** - * Represents [StackNavigator] and [StackNavigationSource] at the same time. + * Represents [StackNavigator] and [NavigationSource] at the same time. */ -interface StackNavigation : StackNavigator, StackNavigationSource +interface StackNavigation : StackNavigator, NavigationSource> { + + class Event( + val transformer: (stack: List) -> List, + val onComplete: (newStack: List, oldStack: List) -> Unit = { _, _ -> }, + ) +} /** * Returns a default implementation of [StackNavigation]. diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigationSource.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigationSource.kt deleted file mode 100644 index a4e09c0cd..000000000 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigationSource.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.arkivanov.decompose.router.stack - -import com.arkivanov.decompose.router.children.NavigationSource - -/** - * Represents a source of navigation events for `Child Stack`. - * - * @see StackNavigator - */ -interface StackNavigationSource : NavigationSource> { - - class Event( - val transformer: (stack: List) -> List, - val onComplete: (newStack: List, oldStack: List) -> Unit = { _, _ -> }, - ) -} From 6e0d2be58d9160ed03d8ec7f8a3cfc6120396f97 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 10 Feb 2024 15:03:38 +0000 Subject: [PATCH 59/89] Updated Essenty to 2.0.0-alpha06 --- .../backhandler/DefaultChildBackHandler.kt | 68 ++++++------------- .../backhandler/TestBackDispatcher.kt | 28 +++----- deps.versions.toml | 2 +- .../PredictiveBackGestureOverlay.kt | 41 +++++------ 4 files changed, 47 insertions(+), 92 deletions(-) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/backhandler/DefaultChildBackHandler.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/backhandler/DefaultChildBackHandler.kt index 53d81b850..851996e87 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/backhandler/DefaultChildBackHandler.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/backhandler/DefaultChildBackHandler.kt @@ -1,7 +1,7 @@ package com.arkivanov.decompose.backhandler import com.arkivanov.essenty.backhandler.BackCallback -import com.arkivanov.essenty.backhandler.BackEvent +import com.arkivanov.essenty.backhandler.BackDispatcher import com.arkivanov.essenty.backhandler.BackHandler import kotlin.properties.Delegates.observable @@ -9,66 +9,38 @@ internal class DefaultChildBackHandler( private val parent: BackHandler, isEnabled: Boolean, priority: Int, -) : ChildBackHandler { + private val delegate: BackDispatcher = BackDispatcher(), +) : ChildBackHandler, BackHandler by delegate { + + private val parentCallback: BackCallback = + BackCallback( + isEnabled = false, + priority = priority, + onBackStarted = delegate::startPredictiveBack, + onBackProgressed = delegate::progressPredictiveBack, + onBackCancelled = delegate::cancelPredictiveBack, + onBack = delegate::back, + ) - private val parentCallback = BackCallbackImpl(priority = priority) - private var set = emptySet() - private val enabledChangedListener: (Boolean) -> Unit = { updateParentCallbackEnabledState() } override var isEnabled: Boolean by observable(isEnabled) { _, _, _ -> updateParentCallbackEnabledState() } - private var isStarted = false + + init { + delegate.addEnabledChangedListener { updateParentCallbackEnabledState() } + } override fun start() { - if (!isStarted) { - isStarted = true + if (!parent.isRegistered(parentCallback)) { parent.register(parentCallback) } } override fun stop() { - if (isStarted) { - isStarted = false + if (parent.isRegistered(parentCallback)) { parent.unregister(parentCallback) } } - override fun register(callback: BackCallback) { - check(callback !in set) { "Callback is already registered" } - - this.set += callback - callback.addEnabledChangedListener(enabledChangedListener) - updateParentCallbackEnabledState() - } - - override fun unregister(callback: BackCallback) { - check(callback in set) { "Callback is not registered" } - - callback.removeEnabledChangedListener(enabledChangedListener) - this.set -= callback - updateParentCallbackEnabledState() - } - private fun updateParentCallbackEnabledState() { - parentCallback.isEnabled = isEnabled && set.any(BackCallback::isEnabled) - } - - private fun Iterable.findMostImportant(): BackCallback? = - sortedBy(BackCallback::priority).lastOrNull(BackCallback::isEnabled) - - private inner class BackCallbackImpl(priority: Int) : BackCallback(isEnabled = false, priority = priority) { - override fun onBackStarted(backEvent: BackEvent) { - set.findMostImportant()?.onBackStarted(backEvent) - } - - override fun onBackProgressed(backEvent: BackEvent) { - set.findMostImportant()?.onBackProgressed(backEvent) - } - - override fun onBackCancelled() { - set.findMostImportant()?.onBackCancelled() - } - - override fun onBack() { - set.findMostImportant()?.onBack() - } + parentCallback.isEnabled = isEnabled && delegate.isEnabled } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/backhandler/TestBackDispatcher.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/backhandler/TestBackDispatcher.kt index 1587dc9fb..9793586d9 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/backhandler/TestBackDispatcher.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/backhandler/TestBackDispatcher.kt @@ -3,31 +3,19 @@ package com.arkivanov.decompose.backhandler import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.backhandler.BackDispatcher -internal class TestBackDispatcher : BackDispatcher { +internal class TestBackDispatcher( + private val delegate: BackDispatcher = BackDispatcher() +) : BackDispatcher by delegate { - private var set = emptySet() - val size: Int get() = set.size - - override val isEnabled: Boolean get() = set.any(BackCallback::isEnabled) + var size: Int = 0 override fun register(callback: BackCallback) { - check(callback !in set) { "Callback is already registered" } - - this.set += callback + delegate.register(callback) + size++ } override fun unregister(callback: BackCallback) { - check(callback in set) { "Callback is not registered" } - - this.set -= callback - } - - override fun back(): Boolean { - set.lastOrNull(BackCallback::isEnabled)?.also { - it.onBack() - return true - } - - return false + delegate.unregister(callback) + size-- } } diff --git a/deps.versions.toml b/deps.versions.toml index f0282424d..cfc0e78c8 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -2,7 +2,7 @@ decompose = "3.0.0-alpha05" kotlin = "1.9.21" -essenty = "2.0.0-alpha03" +essenty = "2.0.0-alpha06" reaktive = "1.2.3" junit = "4.13.2" jetbrainsCompose = "1.6.0-alpha01" diff --git a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt index 93bfc5896..29d316c6e 100644 --- a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt +++ b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt @@ -26,7 +26,6 @@ import androidx.compose.ui.unit.dp import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.backhandler.BackDispatcher -import com.arkivanov.essenty.backhandler.BackDispatcher.PredictiveBackDispatcher import com.arkivanov.essenty.backhandler.BackEvent import com.arkivanov.essenty.backhandler.BackEvent.SwipeEdge @@ -207,8 +206,9 @@ private class BackGestureHandler( } suspend fun AwaitPointerEventScope.handleGesture() { - val dispatcher: PredictiveBackDispatcher = awaitStart() ?: return - processGesture(dispatcher) + if (awaitStart()) { + processGesture() + } } private suspend fun AwaitPointerEventScope.awaitStartChange(): PointerInputChange? { @@ -234,26 +234,22 @@ private class BackGestureHandler( } } - private suspend fun AwaitPointerEventScope.awaitStart(): PredictiveBackDispatcher? { - val change = awaitStartChange() ?: return null - val position = change.position - - val dispatcher = - backDispatcher.startPredictiveBack( - BackEvent( - progress = getProgress(position = position), - swipeEdge = edge.toSwipeEdge(), - touchX = position.x, - touchY = position.y, - ) - ) ?: return null - + private suspend fun AwaitPointerEventScope.awaitStart(): Boolean { + val change = awaitStartChange() ?: return false change.consume() + val position = change.position - return dispatcher + return backDispatcher.startPredictiveBack( + BackEvent( + progress = getProgress(position = position), + swipeEdge = edge.toSwipeEdge(), + touchX = position.x, + touchY = position.y, + ) + ) } - private suspend fun AwaitPointerEventScope.processGesture(dispatcher: PredictiveBackDispatcher) { + private suspend fun AwaitPointerEventScope.processGesture() { while (true) { val change = awaitChange() val position = change.position @@ -261,7 +257,7 @@ private class BackGestureHandler( val progress = getProgress(position = position) - dispatcher.progress( + backDispatcher.progressPredictiveBack( BackEvent( progress = progress, swipeEdge = edge.toSwipeEdge(), @@ -270,12 +266,11 @@ private class BackGestureHandler( ) ) - println("Progress: $progress") if (!change.pressed) { if (progress > progressConfirmationThreshold) { - dispatcher.finish() + backDispatcher.back() } else { - dispatcher.cancel() + backDispatcher.cancelPredictiveBack() } return From 2078ee7cefdea7c7219424214418fdb422b51c69 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 10 Feb 2024 15:25:14 +0000 Subject: [PATCH 60/89] Fixed animation on hardware back button click with predictive back gesture enabled --- .../predictiveback/PredictiveBackAnimation.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt index 610b8475d..c30ff0303 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import com.arkivanov.decompose.Child import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.decompose.Ref import com.arkivanov.decompose.extensions.compose.stack.animation.LocalStackAnimationProvider import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation import com.arkivanov.decompose.extensions.compose.stack.animation.emptyStackAnimation @@ -116,22 +117,30 @@ private class PredictiveBackAnimation( if (isBackEnabled) { if (isBackGestureEnabled) { val scope = rememberCoroutineScope() + val initialBackEventRef = remember { Ref(null) } BackGestureHandler( backHandler = backHandler, - onBackStarted = { - data = data.copy(animatable = selector(it, data.stack.active, data.stack.backStack.last())) - }, + onBackStarted = { initialBackEventRef.value = it }, onBackProgressed = { + initialBackEventRef.value?.also { initialBackEvent -> + data = data.copy(animatable = selector(initialBackEvent, data.stack.active, data.stack.backStack.last())) + initialBackEventRef.value = null + } + scope.launch { data.animatable?.animate(it) } }, onBackCancelled = { + initialBackEventRef.value = null + scope.launch { data.animatable?.cancel() data = data.copy(animatable = null) } }, onBack = { + initialBackEventRef.value = null + if (data.animatable == null) { onBack() } else { From 6fb385a530236e5c4b7d33a74face7df0fa22b27 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 10 Feb 2024 21:04:45 +0000 Subject: [PATCH 61/89] Bumped version to 3.0.0-alpha06 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index cfc0e78c8..7ae20dcf7 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "3.0.0-alpha05" +decompose = "3.0.0-alpha06" kotlin = "1.9.21" essenty = "2.0.0-alpha06" reaktive = "1.2.3" From 1c1c4dd985b9ddddc43d24101bcdf45b9809b6a1 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 16 Feb 2024 18:58:08 +0000 Subject: [PATCH 62/89] Buffer navigation events during initialisation --- .../router/children/ChildrenFactory.kt | 153 +++++++++++++----- .../children/ChildrenBackPressedTest.kt | 50 +++++- .../router/children/ChildrenBasicTest.kt | 80 ++++++++- .../router/children/ChildrenLifecycleTest.kt | 2 +- .../children/ChildrenRetainedInstanceTest.kt | 2 +- .../router/children/ChildrenSavedStateTest.kt | 2 +- .../router/children/ChildrenTestBase.kt | 2 +- 7 files changed, 238 insertions(+), 53 deletions(-) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt index fe801d5ef..f02733b85 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt @@ -2,6 +2,7 @@ package com.arkivanov.decompose.router.children import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.Relay import com.arkivanov.decompose.backhandler.child import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value @@ -114,7 +115,115 @@ fun , S : Any> ComponentContext.child childFactory: (configuration: C, componentContext: ComponentContext) -> T, ): Value { val mainBackHandler = backHandler.child() + val relay = Relay>() + val cancellation = source.subscribe { relay.accept(NavEvent.Event(it)) } + val backCallback = BackCallback { relay.accept(NavEvent.Back) } + val eventProcessor = EventProcessor() + relay.subscribe(eventProcessor::process) + + val holder = + Holder( + navigator = childrenNavigator( + key = key, + initialState = initialState, + saveState = saveState, + restoreState = restoreState, + childFactory = childFactory, + ), + stateMapper = stateMapper, + navTransformer = navTransformer, + onStateChanged = { newState, oldState, isBackEnabled -> + backCallback.isEnabled = isBackEnabled + onStateChanged(newState, oldState) + }, + onEventComplete = onEventComplete, + backTransformer = backTransformer, + ) + + relay.accept(NavEvent.Init(holder)) + mainBackHandler.register(backCallback) + lifecycle.doOnDestroy(cancellation::cancel) + + return holder.state +} + +private class EventProcessor { + private val pendingEvents = ArrayList() + private var holder: Holder<*, *, E, *, *>? = null + + fun process(event: NavEvent) { + when (event) { + is NavEvent.Event -> { + if (holder != null) { + holder?.navigate(event.event) + } else { + pendingEvents += event.event + } + } + + is NavEvent.Back -> holder?.back() + + is NavEvent.Init -> { + holder = event.holder + pendingEvents.forEach(event.holder::navigate) + pendingEvents.clear() + } + } + } +} + +private sealed interface NavEvent { + class Init(val holder: Holder<*, *, E, *, *>) : NavEvent + class Event(val event: E) : NavEvent + data object Back : NavEvent +} + +private class Holder, S : Any>( + private val navigator: ChildrenNavigator, + private val stateMapper: (state: N, children: List>) -> S, + private val navTransformer: (state: N, event: E) -> N, + private val onStateChanged: (newState: N, oldState: N?, isBackEnabled: Boolean) -> Unit, + private val onEventComplete: (event: E, newState: N, oldState: N) -> Unit, + private val backTransformer: (state: N) -> (() -> N)?, +) { + val state: MutableValue = MutableValue(stateMapper(navigator.navState, navigator.children)) + private var bt: (() -> N)? = backTransformer(navigator.navState) + + init { + onStateChanged(navigator.navState, null, bt != null) + } + + fun navigate(event: E) { + val oldState = navigator.navState + navigator.navigate(navState = navTransformer(navigator.navState, event)) + val newState = navigator.navState + onAfterNavigate(newState, oldState) + onEventComplete(event, newState, oldState) + } + + fun back() { + val state = bt?.invoke() ?: return + val oldState = navigator.navState + navigator.navigate(navState = state) + val newState = navigator.navState + onAfterNavigate(newState, oldState) + } + + private fun onAfterNavigate(newState: N, oldState: N) { + bt = backTransformer(newState) + state.value = stateMapper(newState, navigator.children) + onStateChanged(newState, oldState, bt != null) + } +} + +private fun > ComponentContext.childrenNavigator( + key: String, + initialState: () -> N, + saveState: (state: N) -> SerializableContainer?, + restoreState: (container: SerializableContainer) -> N?, + childFactory: (configuration: C, componentContext: ComponentContext) -> T, +): ChildrenNavigator { val navigator = stateKeeper.consume(key = key, strategy = SavedState.serializer()).let { savedState -> val restoredNavState: N? = savedState?.navState?.let(restoreState) @@ -141,49 +250,7 @@ fun , S : Any> ComponentContext.child } } - val state = MutableValue(stateMapper(navigator.navState, navigator.children)) - - var bt: (() -> N)? = backTransformer(navigator.navState) - - lateinit var backCallback: BackCallback - - fun onAfterNavigate(newState: N, oldState: N) { - bt = backTransformer(newState) - backCallback.isEnabled = bt != null - state.value = stateMapper(newState, navigator.children) - onStateChanged(newState, oldState) - } - - backCallback = - BackCallback(isEnabled = bt != null) { - bt?.invoke()?.also { state -> - val oldState = navigator.navState - navigator.navigate(navState = state) - val newState = navigator.navState - onAfterNavigate(newState, oldState) - } - } - - mainBackHandler.register(backCallback) - - val cancellation = - source.subscribe { event -> - val oldState = navigator.navState - navigator.navigate(navState = navTransformer(navigator.navState, event)) - val newState = navigator.navState - onAfterNavigate(newState, oldState) - onEventComplete(event, newState, oldState) - } - - lifecycle.doOnDestroy { - cancellation.cancel() - stateKeeper.unregister(key = key) - mainBackHandler.unregister(backCallback) - } - - onStateChanged(navigator.navState, null) - - return state + return navigator } @Serializable diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt index 712799788..19229abdb 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt @@ -1,8 +1,8 @@ package com.arkivanov.decompose.router.children -import com.arkivanov.decompose.router.children.ChildNavState.Status.RESUMED -import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED import com.arkivanov.decompose.router.children.ChildNavState.Status.CREATED +import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED +import com.arkivanov.decompose.router.children.ChildNavState.Status.RESUMED import com.arkivanov.decompose.router.children.ChildNavState.Status.STARTED import com.arkivanov.decompose.value.getValue import com.arkivanov.essenty.backhandler.BackCallback @@ -12,7 +12,7 @@ import kotlin.test.assertFalse import kotlin.test.assertTrue @Suppress("TestFunctionName") -internal class ChildrenBackPressedTest : ChildrenTestBase() { +class ChildrenBackPressedTest : ChildrenTestBase() { @Test fun GIVEN_children_created_with_resumed_child_and_enabled_callback_registered_WHEN_back_THEN_callback_called() { @@ -341,4 +341,48 @@ internal class ChildrenBackPressedTest : ChildrenTestBase() { assertContentEquals(listOf(stateOf() to stateOf(1 by DESTROYED, 2 by CREATED, 3 by STARTED, 4 by RESUMED)), results) } + + @Test + fun WHEN_back_and_navigate_recursively_THEN_childFactory_not_called_recursively() { + var isNavigating = false + var isCalledRecursively = false + context.children( + initialState = stateOf(1 by DESTROYED, 2 by RESUMED), + backTransformer = { { stateOf(1 by RESUMED) } }, + ) { config, ctx -> + if (isNavigating) { + isCalledRecursively = true + } + + if (config == 1) { + isNavigating = true + navigate { it + (3 by RESUMED) } + isNavigating = false + } + + Component(config, ctx) + } + + backDispatcher.back() + + assertFalse(isCalledRecursively) + } + + @Test + fun WHEN_back_and_navigate_recursively_THEN_state_updated() { + val children by context.children( + initialState = stateOf(1 by DESTROYED, 2 by RESUMED), + backTransformer = { { stateOf(1 by RESUMED) } }, + ) { config, ctx -> + if (config == 1) { + navigate { it + (3 by RESUMED) } + } + + Component(config, ctx) + } + + backDispatcher.back() + + children.assertChildren(1 to 1, 3 to 3) + } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt index c1dc90a45..7081711e7 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt @@ -2,9 +2,10 @@ package com.arkivanov.decompose.router.children import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.router.TestInstance -import com.arkivanov.decompose.router.children.ChildNavState.Status.RESUMED -import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED import com.arkivanov.decompose.router.children.ChildNavState.Status.CREATED +import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED +import com.arkivanov.decompose.router.children.ChildNavState.Status.RESUMED +import com.arkivanov.decompose.router.children.ChildNavState.Status.STARTED import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher import com.arkivanov.decompose.value.getValue import com.arkivanov.essenty.instancekeeper.getOrCreate @@ -12,9 +13,10 @@ import com.arkivanov.essenty.lifecycle.destroy import com.arkivanov.essenty.lifecycle.doOnDestroy import kotlin.test.Test import kotlin.test.assertContentEquals +import kotlin.test.assertFalse @Suppress("TestFunctionName") -internal class ChildrenBasicTest : ChildrenTestBase() { +class ChildrenBasicTest : ChildrenTestBase() { @Test fun WHEN_navigate_THEN_onEventComplete_called() { @@ -142,4 +144,76 @@ internal class ChildrenBasicTest : ChildrenTestBase() { assertContentEquals(listOf("component", "instance"), destroyEvents) } + + @Test + fun WHEN_navigate_and_navigate_recursively_THEN_childFactory_not_called_recursively() { + var isNavigating = false + var isCalledRecursively = false + context.children(initialState = stateOf(1 by CREATED)) { config, ctx -> + if (isNavigating) { + isCalledRecursively = true + } + + if (config == 2) { + isNavigating = true + navigate { it + (3 by RESUMED) } + isNavigating = false + } + + Component(config, ctx) + } + + navigate { it + listOf(2 by STARTED) } + + assertFalse(isCalledRecursively) + } + + @Test + fun WHEN_navigate_and_navigate_recursively_THEN_state_updated() { + val children = context.children(initialState = stateOf(1 by CREATED)) { config, ctx -> + if (config == 2) { + navigate { it + (3 by RESUMED) } + } + + Component(config, ctx) + } + + navigate { it + (2 by STARTED) } + + children.value.assertChildren(1 to 1, 2 to 2, 3 to 3) + } + + @Test + fun WHEN_navigate_recursively_during_init_THEN_childFactory_not_called_recursively() { + var isNavigating = false + var isCalledRecursively = false + context.children(initialState = stateOf(1 by CREATED)) { config, ctx -> + if (isNavigating) { + isCalledRecursively = true + } + + if (config == 1) { + isNavigating = true + navigate { it + listOf(2 by RESUMED) } + isNavigating = false + } + + Component(config, ctx) + } + + assertFalse(isCalledRecursively) + } + + @Test + fun WHEN_navigate_recursively_during_init_THEN_state_updated() { + val children by context.children(initialState = stateOf(1 by CREATED)) { config, ctx -> + if (config == 1) { + navigate { it + (2 by RESUMED) } + } + + Component(config, ctx) + } + + children.assertChildren(1 to 1, 2 to 2) + } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt index 3b10394a1..fe34fcea5 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt @@ -12,7 +12,7 @@ import kotlin.test.Test import kotlin.test.assertEquals @Suppress("TestFunctionName") -internal class ChildrenLifecycleTest : ChildrenTestBase() { +class ChildrenLifecycleTest : ChildrenTestBase() { @Test fun WHEN_created_THEN_initial_state() { diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt index 28d420dd3..1ca246db4 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt @@ -17,7 +17,7 @@ import kotlin.test.assertSame import kotlin.test.assertTrue @Suppress("TestFunctionName") -internal class ChildrenRetainedInstanceTest : ChildrenTestBase() { +class ChildrenRetainedInstanceTest : ChildrenTestBase() { @Test fun WHEN_child_switched_from_created_to_destroyed_THEN_instance_destroyed() { diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt index 55c2022ec..ed64c127d 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt @@ -16,7 +16,7 @@ import kotlin.test.assertNull import kotlin.test.assertTrue @Suppress("TestFunctionName") -internal class ChildrenSavedStateTest : ChildrenTestBase() { +class ChildrenSavedStateTest : ChildrenTestBase() { @Test fun WHEN_child_switched_from_resumed_to_started_THEN_state_not_saved() { diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt index 6c5e26c1d..882a99daa 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt @@ -15,7 +15,7 @@ import kotlinx.serialization.Serializable import kotlin.test.BeforeTest import kotlin.test.assertContentEquals -internal open class ChildrenTestBase { +open class ChildrenTestBase { private val navigation = SimpleNavigation<(TestNavState) -> TestNavState>() protected val lifecycle = LifecycleRegistry() From 6ca0a43c1531944adcee2c093c861ea70c5b9de8 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 16 Feb 2024 20:38:16 +0000 Subject: [PATCH 63/89] Don't remove the first configuration in popWhile --- .../decompose/router/stack/StackNavigatorExt.kt | 8 ++++++-- .../decompose/router/stack/RouterPopWhileTest.kt | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt index 7cbd82f92..34a3391db 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt @@ -80,13 +80,17 @@ fun StackNavigator.pop(onComplete: (isSuccess: Boolean) -> Unit = { /** * Drops the configurations at the top of the stack while the [predicate] returns `true`. + * If the [predicate] returns `true` for every configuration, then the first (oldest) configuration + * remains in the stack. */ inline fun StackNavigator.popWhile(crossinline predicate: (C) -> Boolean) { popWhile(predicate = predicate, onComplete = {}) } /** - * Drops the configurations at the top of the stack while the [predicate] returns true + * Drops the configurations at the top of the stack while the [predicate] returns true. + * If the [predicate] returns `true` for every configuration, then the first (oldest) configuration + * remains in the stack. * * @param onComplete called when the navigation is finished (either synchronously or asynchronously). * The `isSuccess` argument is `true` if at least one component has been popped. @@ -96,7 +100,7 @@ inline fun StackNavigator.popWhile( crossinline onComplete: (isSuccess: Boolean) -> Unit, ) { navigate( - transformer = { stack -> stack.dropLastWhile(predicate) }, + transformer = { stack -> stack.dropLastWhile(predicate).takeUnless(List<*>::isEmpty) ?: stack.take(1) }, onComplete = { newStack, oldStack -> onComplete(newStack.size < oldStack.size) }, ) } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterPopWhileTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterPopWhileTest.kt index a1440871f..2f15efd70 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterPopWhileTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterPopWhileTest.kt @@ -25,4 +25,13 @@ class RouterPopWhileTest { assertTrue(isCalled) } + + @Test + fun WHEN_popWhile_all_THEN_first_child_not_removed() { + val navigator = TestStackNavigator(listOf(1, 2, 3, 4)) + + navigator.popWhile { true } + + assertEquals(listOf(1), navigator.configurations) + } } From 430ec194d9fd68d31acecab418e7a57c4cd6b7a0 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 16 Feb 2024 21:22:20 +0000 Subject: [PATCH 64/89] Added tests for bringToFront with base class --- .../router/stack/RouterBringToFrontTest.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterBringToFrontTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterBringToFrontTest.kt index 09aef9c82..53dac9103 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterBringToFrontTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterBringToFrontTest.kt @@ -78,9 +78,24 @@ class RouterBringToFrontTest { assertEquals(listOf(Config.A, Config.B, Config.C(1)), navigator.configurations) } + @Test + fun WHEN_bringToFront_base_class_THEN_pushed() { + val baseConfig = BaseConfig() + val navigator = TestStackNavigator(listOf(BaseConfig.A, BaseConfig.B)) + + navigator.bringToFront(baseConfig) + + assertEquals(listOf(BaseConfig.A, BaseConfig.B, baseConfig), navigator.configurations) + } + private sealed class Config { data object A : Config() data object B : Config() data class C(val value: Int) : Config() } + + private open class BaseConfig { + data object A : BaseConfig() + data object B : BaseConfig() + } } From 50a795a603cea07c5849dc73a2b2d21d3d291773 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 17 Feb 2024 09:44:57 +0000 Subject: [PATCH 65/89] Inlined the rest of navigation extensions with lambdas --- .../decompose/router/slot/SlotNavigatorExt.kt | 4 ++-- .../router/stack/StackNavigatorExt.kt | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigatorExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigatorExt.kt index f5d53f678..cb2a6d41c 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigatorExt.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/SlotNavigatorExt.kt @@ -14,7 +14,7 @@ fun SlotNavigator.navigate(transformer: (configuration: C?) -> C?) * * @param onComplete called when the navigation is finished (either synchronously or asynchronously). */ -fun SlotNavigator.activate(configuration: C, onComplete: () -> Unit = {}) { +inline fun SlotNavigator.activate(configuration: C, crossinline onComplete: () -> Unit = {}) { navigate(transformer = { configuration }, onComplete = { _, _ -> onComplete() }) } @@ -24,7 +24,7 @@ fun SlotNavigator.activate(configuration: C, onComplete: () -> Unit * @param onComplete called when the navigation is finished (either synchronously or asynchronously). * The `isSuccess` argument is `true` if there was an active child component, `false` otherwise. */ -fun SlotNavigator<*>.dismiss(onComplete: (isSuccess: Boolean) -> Unit = {}) { +inline fun SlotNavigator<*>.dismiss(crossinline onComplete: (isSuccess: Boolean) -> Unit = {}) { navigate( transformer = { null }, onComplete = { _, oldConfiguration -> onComplete(oldConfiguration != null) }, diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt index 34a3391db..6b7f2f2b9 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt @@ -16,7 +16,7 @@ fun StackNavigator.navigate(transformer: (stack: List) -> List StackNavigator.push(configuration: C, onComplete: () -> Unit = {}) { +inline fun StackNavigator.push(configuration: C, crossinline onComplete: () -> Unit = {}) { navigate(transformer = { it + configuration }, onComplete = { _, _ -> onComplete() }) } @@ -34,9 +34,9 @@ fun StackNavigator.push(configuration: C, onComplete: () -> Unit = * The `isSuccess` argument is `true` if the component was pushed, `false` otherwise. */ @ExperimentalDecomposeApi -fun StackNavigator.pushNew( +inline fun StackNavigator.pushNew( configuration: C, - onComplete: (isSuccess: Boolean) -> Unit = {}, + crossinline onComplete: (isSuccess: Boolean) -> Unit = {}, ) { navigate( transformer = { stack -> if (stack.last() == configuration) stack else stack + configuration }, @@ -54,9 +54,9 @@ fun StackNavigator.pushNew( * @param onComplete called when the navigation is finished (either synchronously or asynchronously). */ @ExperimentalDecomposeApi -fun StackNavigator.pushToFront( +inline fun StackNavigator.pushToFront( configuration: C, - onComplete: () -> Unit = {}, + crossinline onComplete: () -> Unit = {}, ) { navigate( transformer = { stack -> stack - configuration + configuration }, @@ -71,7 +71,7 @@ fun StackNavigator.pushToFront( * The `isSuccess` argument is `true` if the stack size was greater than 1 and a component was popped, * `false` otherwise. */ -fun StackNavigator.pop(onComplete: (isSuccess: Boolean) -> Unit = {}) { +inline fun StackNavigator.pop(crossinline onComplete: (isSuccess: Boolean) -> Unit = {}) { navigate( transformer = { stack -> stack.takeIf { it.size > 1 }?.dropLast(1) ?: stack }, onComplete = { newStack, oldStack -> onComplete(newStack.size < oldStack.size) }, @@ -129,7 +129,7 @@ inline fun StackNavigator.popTo( * * @param onComplete called when the navigation is finished (either synchronously or asynchronously). */ -fun StackNavigator.replaceCurrent(configuration: C, onComplete: () -> Unit = {}) { +inline fun StackNavigator.replaceCurrent(configuration: C, crossinline onComplete: () -> Unit = {}) { navigate( transformer = { it.dropLast(1) + configuration }, onComplete = { _, _ -> onComplete() }, @@ -141,7 +141,7 @@ fun StackNavigator.replaceCurrent(configuration: C, onComplete: () * * @param onComplete called when the navigation is finished (either synchronously or asynchronously). */ -fun StackNavigator.replaceAll(vararg configurations: C, onComplete: () -> Unit = { }) { +inline fun StackNavigator.replaceAll(vararg configurations: C, crossinline onComplete: () -> Unit = { }) { navigate(transformer = { configurations.toList() }, onComplete = { _, _ -> onComplete() }) } @@ -149,7 +149,7 @@ fun StackNavigator.replaceAll(vararg configurations: C, onComplete: * Removes all components with configurations of [configuration]'s class, and adds the provided [configuration] to the top of the stack. * The operation is performed as one transaction. If there is already a component with the same configuration, it will not be recreated. */ -fun StackNavigator.bringToFront(configuration: C, onComplete: () -> Unit = {}) { +inline fun StackNavigator.bringToFront(configuration: C, crossinline onComplete: () -> Unit = {}) { navigate( transformer = { stack -> stack.filterNot { it::class == configuration::class } + configuration }, onComplete = { _, _ -> onComplete() }, From 1358dfe7391c9e297e212608978f8e7a9225ed89 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 17 Feb 2024 11:46:21 +0000 Subject: [PATCH 66/89] Updated Kotlin to 1.9.22, Compose to 1.6.0-rc02, also updated Pages Composable API --- deps.versions.toml | 10 +++------- .../api/android/extensions-compose.api | 2 +- extensions-compose/api/jvm/extensions-compose.api | 2 +- extensions-compose/build.gradle.kts | 3 +++ .../decompose/extensions/compose/pages/Pages.kt | 13 ++++++------- .../decompose/extensions/compose/pages/PagesTest.kt | 2 +- .../com/arkivanov/decompose/sample/app/Main.kt | 7 ++++--- sample/shared/compose/build.gradle.kts | 3 +++ 8 files changed, 22 insertions(+), 20 deletions(-) diff --git a/deps.versions.toml b/deps.versions.toml index 7ae20dcf7..fd9135e14 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,17 +1,15 @@ [versions] decompose = "3.0.0-alpha06" -kotlin = "1.9.21" +kotlin = "1.9.22" essenty = "2.0.0-alpha06" reaktive = "1.2.3" junit = "4.13.2" -jetbrainsCompose = "1.6.0-alpha01" +jetbrainsCompose = "1.6.0-rc02" jetbrainsKotlinWrappers = "1.0.0-pre.608" -jetbrainsKotlinxCoroutines = "1.8.0-RC" +jetbrainsKotlinxCoroutines = "1.8.0" jetbrainsKotlinxSerialization = "1.6.2" jetbrainsBinaryCompatibilityValidator = "0.13.2" -jetpackCompose = "1.5.0" -jetpackComposeCompiler = "1.5.6" robolectric = "4.9.1" androidGradle = "8.0.2" androidMaterial = "1.6.1" @@ -51,8 +49,6 @@ jetbrains-kotlinx-kotlinxSerializationJson = { group = "org.jetbrains.kotlinx", robolectric-robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } -androidx-compose-runtime-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "jetpackCompose" } - android-gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradle" } android-material-material = { group = "com.google.android.material", name = "material", version.ref = "androidMaterial" } android-play-core = { group = "com.google.android.play", name = "core", version.ref = "androidPlay" } diff --git a/extensions-compose/api/android/extensions-compose.api b/extensions-compose/api/android/extensions-compose.api index 0924f0299..a77af59ee 100644 --- a/extensions-compose/api/android/extensions-compose.api +++ b/extensions-compose/api/android/extensions-compose.api @@ -12,7 +12,7 @@ public final class com/arkivanov/decompose/extensions/compose/pages/ComposableSi } public final class com/arkivanov/decompose/extensions/compose/pages/PagesKt { - public static final fun Pages (Landroidx/compose/runtime/State;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V + public static final fun Pages (Lcom/arkivanov/decompose/router/pages/ChildPages;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V public static final fun Pages (Lcom/arkivanov/decompose/value/Value;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V public static final fun defaultHorizontalPager ()Lkotlin/jvm/functions/Function6; public static final fun defaultVerticalPager ()Lkotlin/jvm/functions/Function6; diff --git a/extensions-compose/api/jvm/extensions-compose.api b/extensions-compose/api/jvm/extensions-compose.api index 2c57ea641..3f4187b57 100644 --- a/extensions-compose/api/jvm/extensions-compose.api +++ b/extensions-compose/api/jvm/extensions-compose.api @@ -16,7 +16,7 @@ public final class com/arkivanov/decompose/extensions/compose/pages/ComposableSi } public final class com/arkivanov/decompose/extensions/compose/pages/PagesKt { - public static final fun Pages (Landroidx/compose/runtime/State;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V + public static final fun Pages (Lcom/arkivanov/decompose/router/pages/ChildPages;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V public static final fun Pages (Lcom/arkivanov/decompose/value/Value;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;Lcom/arkivanov/decompose/extensions/compose/pages/PagesScrollAnimation;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;II)V public static final fun defaultHorizontalPager ()Lkotlin/jvm/functions/Function6; public static final fun defaultVerticalPager ()Lkotlin/jvm/functions/Function6; diff --git a/extensions-compose/build.gradle.kts b/extensions-compose/build.gradle.kts index 6fd8b895c..dc40ca934 100644 --- a/extensions-compose/build.gradle.kts +++ b/extensions-compose/build.gradle.kts @@ -48,6 +48,9 @@ kotlin { common.main.dependencies { implementation(project(":decompose")) implementation(compose.foundation) + + // Required due to https://github.com/JetBrains/compose-multiplatform/issues/4326 + implementation(deps.jetbrains.kotlinx.kotlinxCoroutinesCore) } android.main.dependencies { diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/pages/Pages.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/pages/Pages.kt index 48f6b3778..815b9bae1 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/pages/Pages.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/pages/Pages.kt @@ -36,7 +36,7 @@ fun Pages( key: (Child) -> Any = { it.configuration.hashString() }, pageContent: @Composable PagerScope.(index: Int, page: T) -> Unit, ) { - val state = pages.subscribeAsState() + val state by pages.subscribeAsState() Pages( pages = state, @@ -56,7 +56,7 @@ fun Pages( @ExperimentalDecomposeApi @Composable fun Pages( - pages: State>, + pages: ChildPages, onPageSelected: (index: Int) -> Unit, modifier: Modifier = Modifier, scrollAnimation: PagesScrollAnimation = PagesScrollAnimation.Disabled, @@ -64,11 +64,10 @@ fun Pages( key: (Child) -> Any = { it.configuration.hashString() }, pageContent: @Composable PagerScope.(index: Int, page: T) -> Unit, ) { - val childPages by pages - val selectedIndex = childPages.selectedIndex + val selectedIndex = pages.selectedIndex val state = rememberPagerState( initialPage = selectedIndex, - pageCount = { childPages.items.size }, + pageCount = { pages.items.size }, ) LaunchedEffect(selectedIndex) { @@ -92,9 +91,9 @@ fun Pages( pager( modifier, state, - { key(childPages.items[it]) }, + { key(pages.items[it]) }, ) { pageIndex -> - val item = childPages.items[pageIndex] + val item = pages.items[pageIndex] val pageRef = remember(item.configuration) { Ref(item.instance) } if (item.instance != null) { diff --git a/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/pages/PagesTest.kt b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/pages/PagesTest.kt index 84824c729..9edb0db4f 100644 --- a/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/pages/PagesTest.kt +++ b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/pages/PagesTest.kt @@ -151,7 +151,7 @@ class PagesTest { ) { composeRule.setContent { Pages( - pages = pages, + pages = pages.value, onPageSelected = onPageSelected, scrollAnimation = scrollAnimation, ) { index, page -> diff --git a/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt b/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt index 815b5f6e8..50168a24c 100644 --- a/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt +++ b/sample/app-js-compose/src/jsMain/kotlin/com/arkivanov/decompose/sample/app/Main.kt @@ -1,8 +1,9 @@ package com.arkivanov.decompose.sample.app import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.window.Window +import androidx.compose.ui.window.CanvasBasedWindow import com.arkivanov.decompose.DefaultComponentContext import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.router.stack.webhistory.DefaultWebHistoryController @@ -18,7 +19,7 @@ import web.dom.DocumentVisibilityState import web.dom.document import web.events.EventType -@OptIn(ExperimentalDecomposeApi::class) +@OptIn(ExperimentalDecomposeApi::class, ExperimentalComposeUiApi::class) fun main() { val lifecycle = LifecycleRegistry() @@ -33,7 +34,7 @@ fun main() { lifecycle.attachToDocument() onWasmReady { - Window("Decompose Sample") { + CanvasBasedWindow(title = "Decompose Sample") { RootContent(component = root, modifier = Modifier.fillMaxSize()) } } diff --git a/sample/shared/compose/build.gradle.kts b/sample/shared/compose/build.gradle.kts index 6fde2428e..22f44d466 100644 --- a/sample/shared/compose/build.gradle.kts +++ b/sample/shared/compose/build.gradle.kts @@ -63,6 +63,9 @@ kotlin { implementation(compose.foundation) implementation(compose.material) implementation(compose.ui) + + // Required due to https://github.com/JetBrains/compose-multiplatform/issues/4326 + api(deps.jetbrains.kotlinx.kotlinxCoroutinesCore) } jvm.main.dependencies { From 7e1fce2afa2fd869d757193483ee42939451d2cc Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 18 Feb 2024 21:52:26 +0000 Subject: [PATCH 67/89] Added AndroidPredictiveBackAnimatable --- .../api/android/extensions-compose.api | 5 + .../api/jvm/extensions-compose.api | 5 + ...outCorners.kt => LayoutCorners.android.kt} | 0 .../AndroidPredictiveBackAnimatable.kt | 149 ++++++++++++++++++ .../animation/predictiveback/LayoutCorners.kt | 12 ++ .../MaterialPredictiveBackAnimatable.kt | 8 +- ...Corners.kt => LayoutCorners.nonAndroid.kt} | 0 7 files changed, 175 insertions(+), 4 deletions(-) rename extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/{LayoutCorners.kt => LayoutCorners.android.kt} (100%) create mode 100644 extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt rename extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/{LayoutCorners.kt => LayoutCorners.nonAndroid.kt} (100%) diff --git a/extensions-compose/api/android/extensions-compose.api b/extensions-compose/api/android/extensions-compose.api index a77af59ee..574173e08 100644 --- a/extensions-compose/api/android/extensions-compose.api +++ b/extensions-compose/api/android/extensions-compose.api @@ -111,6 +111,11 @@ public final class com/arkivanov/decompose/extensions/compose/stack/animation/St public static synthetic fun stackAnimator$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; } +public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatableKt { + public static final fun androidPredictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; + public static synthetic fun androidPredictiveBackAnimatable$default (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; +} + public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatableKt { public static final fun materialPredictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; public static synthetic fun materialPredictiveBackAnimatable$default (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; diff --git a/extensions-compose/api/jvm/extensions-compose.api b/extensions-compose/api/jvm/extensions-compose.api index 3f4187b57..001c819bb 100644 --- a/extensions-compose/api/jvm/extensions-compose.api +++ b/extensions-compose/api/jvm/extensions-compose.api @@ -115,6 +115,11 @@ public final class com/arkivanov/decompose/extensions/compose/stack/animation/St public static synthetic fun stackAnimator$default (Landroidx/compose/animation/core/FiniteAnimationSpec;Lkotlin/jvm/functions/Function5;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/StackAnimator; } +public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatableKt { + public static final fun androidPredictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; + public static synthetic fun androidPredictiveBackAnimatable$default (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; +} + public final class com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatableKt { public static final fun materialPredictiveBackAnimatable (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; public static synthetic fun materialPredictiveBackAnimatable$default (Lcom/arkivanov/essenty/backhandler/BackEvent;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimatable; diff --git a/extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt b/extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.android.kt similarity index 100% rename from extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt rename to extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.android.kt diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt new file mode 100644 index 000000000..6c8817f10 --- /dev/null +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt @@ -0,0 +1,149 @@ +package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback + +import androidx.compose.animation.core.Animatable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.GraphicsLayerScope +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.util.lerp +import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.essenty.backhandler.BackEvent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch + +/** + * Creates an implementation of [PredictiveBackAnimatable] that resembles the standard animation on + * some Android devices (e.g. in system settings on Pixel phones). + * + * @param initialBackEvent an initial [BackEvent] of the predictive back gesture. + * @param exitShape an optional clipping shape of the child being removed (the currently active child). + * If not supplied then a [RoundedCornerShape][androidx.compose.foundation.shape.RoundedCornerShape] will be applied. + * @param enterShape an optional clipping shape of the child being shown (the previous child). + * If not supplied then a [RoundedCornerShape][androidx.compose.foundation.shape.RoundedCornerShape] will be applied. + */ +@ExperimentalDecomposeApi +fun androidPredictiveBackAnimatable( + initialBackEvent: BackEvent, + exitShape: ((progress: Float, edge: BackEvent.SwipeEdge) -> Shape)? = null, + enterShape: ((progress: Float, edge: BackEvent.SwipeEdge) -> Shape)? = null, +): PredictiveBackAnimatable = + AndroidPredictiveBackAnimatable( + initialEvent = initialBackEvent, + exitShape = exitShape, + enterShape = enterShape, + ) + +@ExperimentalDecomposeApi +private class AndroidPredictiveBackAnimatable( + initialEvent: BackEvent, + private val exitShape: ((progress: Float, edge: BackEvent.SwipeEdge) -> Shape)? = null, + private val enterShape: ((progress: Float, edge: BackEvent.SwipeEdge) -> Shape)? = null, +) : PredictiveBackAnimatable { + + private val exitProgressAnimatable = Animatable(initialValue = initialEvent.progress.exitProgress()) + private val exitProgress: Float by derivedStateOf { exitProgressAnimatable.value } + private val enterProgressAnimatable = Animatable(initialValue = initialEvent.progress.enterProgress()) + private val enterProgress: Float by derivedStateOf { enterProgressAnimatable.value } + private val enterFinishProgressAnimatable = Animatable(initialValue = 0F) + private val enterFinishProgress: Float by derivedStateOf { enterFinishProgressAnimatable.value } + private var edge by mutableStateOf(initialEvent.swipeEdge) + + override val exitModifier: Modifier + get() = + if (exitShape == null) { + Modifier.withLayoutCorners { corners -> + graphicsLayer { + setupExitGraphicLayer { progress, _ -> corners.toShape(progress) } + } + } + } else { + Modifier.graphicsLayer { + setupExitGraphicLayer(exitShape) + } + } + + override val enterModifier: Modifier + get() = + if (enterShape == null) { + Modifier.withLayoutCorners { corners -> + graphicsLayer { + setupEnterGraphicLayer { progress, _ -> corners.toShape(progress) } + } + } + } else { + Modifier.graphicsLayer { + setupEnterGraphicLayer(enterShape) + } + } + + private fun GraphicsLayerScope.setupExitGraphicLayer(layoutShape: (progress: Float, edge: BackEvent.SwipeEdge) -> Shape) { + alpha = 1F - exitProgress + scaleX = 1F - exitProgress * 0.1F + scaleY = scaleX + translationX = size.width * 0.5F * exitProgress + shape = layoutShape(exitProgress, edge) + clip = true + } + + private fun GraphicsLayerScope.setupEnterGraphicLayer(layoutShape: (progress: Float, edge: BackEvent.SwipeEdge) -> Shape) { + val totalProgress = enterProgress.plusFinishProgress(enterFinishProgress) + alpha = totalProgress + scaleX = lerp(start = 0.95F, stop = 0.90F, fraction = enterProgress).plusFinishProgress(enterFinishProgress) + scaleY = scaleX + translationX = lerp(start = -size.width * 0.15F, stop = 0F, fraction = totalProgress) + shape = layoutShape(totalProgress, edge) + clip = true + } + + private fun Float.plusFinishProgress(progress: Float): Float = + (this + (1F - this) * progress).coerceIn(0F, 1F) + + override suspend fun animate(event: BackEvent) { + edge = event.swipeEdge + + awaitAll( + { exitProgressAnimatable.animateTo(targetValue = event.progress.exitProgress()) }, + { enterProgressAnimatable.animateTo(targetValue = event.progress.enterProgress()) }, + ) + } + + override suspend fun finish() { + awaitAll( + { exitProgressAnimatable.animateTo(targetValue = 1F) }, + { enterFinishProgressAnimatable.animateTo(targetValue = 1F) }, + ) + } + + override suspend fun cancel() { + awaitAll( + { exitProgressAnimatable.animateTo(targetValue = 0F) }, + { enterProgressAnimatable.animateTo(targetValue = 0F) }, + ) + } + + private suspend fun awaitAll(vararg actions: suspend CoroutineScope.() -> Unit) { + coroutineScope { + actions.map { launch(block = it) }.joinAll() + } + } + + private fun Float.exitProgress(): Float = + if (this < PROGRESS_THRESHOLD) this else 1F + + private fun Float.enterProgress(): Float = + if (this < PROGRESS_THRESHOLD) { + 0F + } else { + 1F - 0.6F * (1F - this) / (1F - PROGRESS_THRESHOLD) + } + + private companion object { + private const val PROGRESS_THRESHOLD = 0.05F + } +} diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt index 8a95c7c3a..1dd24b939 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt @@ -1,5 +1,6 @@ package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -17,3 +18,14 @@ internal data class LayoutCorner( val radius: Dp = 16.dp, val isFixed: Boolean = true, ) + +internal fun LayoutCorners.toShape(progress: Float): RoundedCornerShape = + RoundedCornerShape( + topStart = topStart.getProgressRadius(progress), + topEnd = topEnd.getProgressRadius(progress), + bottomEnd = bottomEnd.getProgressRadius(progress), + bottomStart = bottomStart.getProgressRadius(progress), + ) + +private fun LayoutCorner.getProgressRadius(progress: Float): Dp = + if (isFixed) radius else radius * progress diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt index 20675c2af..7681fc273 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt @@ -37,13 +37,13 @@ fun materialPredictiveBackAnimatable( ): PredictiveBackAnimatable = MaterialPredictiveBackAnimatable( initialEvent = initialBackEvent, - shape = shape, + exitShape = shape, ) @ExperimentalDecomposeApi private class MaterialPredictiveBackAnimatable( private val initialEvent: BackEvent, - private val shape: ((progress: Float, edge: BackEvent.SwipeEdge) -> Shape)? = null, + private val exitShape: ((progress: Float, edge: BackEvent.SwipeEdge) -> Shape)? = null, ) : PredictiveBackAnimatable { private val finishProgressAnimatable = Animatable(initialValue = 1F) @@ -55,13 +55,13 @@ private class MaterialPredictiveBackAnimatable( override val exitModifier: Modifier get() = - if (shape == null) { + if (exitShape == null) { Modifier.withLayoutCorners { corners -> graphicsLayer { setupExitGraphicLayer(corners.toShape()) } } } else { Modifier.graphicsLayer { - setupExitGraphicLayer(this@MaterialPredictiveBackAnimatable.shape.invoke(progress, edge)) + setupExitGraphicLayer(exitShape.invoke(progress, edge)) } } diff --git a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.nonAndroid.kt similarity index 100% rename from extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt rename to extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.nonAndroid.kt From 2d7b041a106e8b064dc15452bd0086edee9e10a7 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Tue, 20 Feb 2024 12:28:16 +0000 Subject: [PATCH 68/89] Updated Essenty to 2.0.0-alpha07 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index fd9135e14..09c48f293 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -2,7 +2,7 @@ decompose = "3.0.0-alpha06" kotlin = "1.9.22" -essenty = "2.0.0-alpha06" +essenty = "2.0.0-alpha07" reaktive = "1.2.3" junit = "4.13.2" jetbrainsCompose = "1.6.0-rc02" From a7921d5bb367367da75e41448a729910ff1c0a72 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 15 Feb 2024 21:23:24 +0000 Subject: [PATCH 69/89] Removed ApplicationLifecycle and used one from Essenty --- .../lifecycle/ApplicationLifecycle.kt | 70 ------------------- sample/shared/compose/build.gradle.kts | 1 + sample/shared/shared/build.gradle.kts | 1 + 3 files changed, 2 insertions(+), 70 deletions(-) delete mode 100644 decompose/src/itvosMain/kotlin/com/arkivanov/decompose/lifecycle/ApplicationLifecycle.kt diff --git a/decompose/src/itvosMain/kotlin/com/arkivanov/decompose/lifecycle/ApplicationLifecycle.kt b/decompose/src/itvosMain/kotlin/com/arkivanov/decompose/lifecycle/ApplicationLifecycle.kt deleted file mode 100644 index a02f80837..000000000 --- a/decompose/src/itvosMain/kotlin/com/arkivanov/decompose/lifecycle/ApplicationLifecycle.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.arkivanov.decompose.lifecycle - -import com.arkivanov.decompose.ExperimentalDecomposeApi -import com.arkivanov.essenty.lifecycle.Lifecycle -import com.arkivanov.essenty.lifecycle.LifecycleRegistry -import com.arkivanov.essenty.lifecycle.create -import com.arkivanov.essenty.lifecycle.destroy -import com.arkivanov.essenty.lifecycle.doOnDestroy -import com.arkivanov.essenty.lifecycle.pause -import com.arkivanov.essenty.lifecycle.resume -import com.arkivanov.essenty.lifecycle.start -import com.arkivanov.essenty.lifecycle.stop -import platform.Foundation.NSNotification -import platform.Foundation.NSNotificationCenter -import platform.Foundation.NSNotificationName -import platform.Foundation.NSOperationQueue -import platform.UIKit.UIApplication -import platform.UIKit.UIApplicationDidBecomeActiveNotification -import platform.UIKit.UIApplicationDidEnterBackgroundNotification -import platform.UIKit.UIApplicationState -import platform.UIKit.UIApplicationWillEnterForegroundNotification -import platform.UIKit.UIApplicationWillResignActiveNotification -import platform.UIKit.UIApplicationWillTerminateNotification -import platform.darwin.NSObjectProtocol - -/** - * An implementation of [Lifecycle] that follows the [UIApplication][platform.UIKit.UIApplication] lifecycle notifications. - */ -@ExperimentalDecomposeApi -class ApplicationLifecycle private constructor( - private val lifecycle: LifecycleRegistry, -) : Lifecycle by lifecycle { - - constructor() : this(lifecycle = LifecycleRegistry()) - - private val willEnterForegroundObserver = addObserver(UIApplicationWillEnterForegroundNotification) { lifecycle.start() } - private val didBecomeActiveObserver = addObserver(UIApplicationDidBecomeActiveNotification) { lifecycle.resume() } - private val willResignActiveObserver = addObserver(UIApplicationWillResignActiveNotification) { lifecycle.pause() } - private val didEnterBackgroundObserver = addObserver(UIApplicationDidEnterBackgroundNotification) { lifecycle.stop() } - private val willTerminateObserver = addObserver(UIApplicationWillTerminateNotification) { lifecycle.destroy() } - - init { - NSOperationQueue.mainQueue.addOperationWithBlock { - if (lifecycle.state == Lifecycle.State.INITIALIZED) { - when (UIApplication.sharedApplication.applicationState) { - UIApplicationState.UIApplicationStateActive -> lifecycle.resume() - UIApplicationState.UIApplicationStateInactive -> lifecycle.start() - UIApplicationState.UIApplicationStateBackground -> lifecycle.create() - else -> lifecycle.create() - } - } - } - - doOnDestroy { - NSNotificationCenter.defaultCenter.removeObserver(willEnterForegroundObserver) - NSNotificationCenter.defaultCenter.removeObserver(didBecomeActiveObserver) - NSNotificationCenter.defaultCenter.removeObserver(willResignActiveObserver) - NSNotificationCenter.defaultCenter.removeObserver(didEnterBackgroundObserver) - NSNotificationCenter.defaultCenter.removeObserver(willTerminateObserver) - } - } - - private fun addObserver(name: NSNotificationName, block: (NSNotification?) -> Unit): NSObjectProtocol = - NSNotificationCenter.defaultCenter.addObserverForName( - name = name, - `object` = null, - queue = NSOperationQueue.mainQueue, - usingBlock = block, - ) -} diff --git a/sample/shared/compose/build.gradle.kts b/sample/shared/compose/build.gradle.kts index 22f44d466..679060cba 100644 --- a/sample/shared/compose/build.gradle.kts +++ b/sample/shared/compose/build.gradle.kts @@ -35,6 +35,7 @@ kotlin { baseName = "Shared" // Used by app-ios-compose export(project(":decompose")) export(project(":sample:shared:shared")) + export(deps.essenty.lifecycle) // Optional, only if you need Predictive Back Gesture on Darwin (Apple) targets export(deps.essenty.backHandler) diff --git a/sample/shared/shared/build.gradle.kts b/sample/shared/shared/build.gradle.kts index defcd3c8a..44a6542fb 100644 --- a/sample/shared/shared/build.gradle.kts +++ b/sample/shared/shared/build.gradle.kts @@ -34,6 +34,7 @@ kotlin { framework { baseName = "Shared" // Used by app-ios export(project(":decompose")) + export(deps.essenty.lifecycle) // Optional, only if you need state preservation on Darwin (Apple) targets export(deps.essenty.stateKeeper) From 6c75b47ad78701dd2971962ad54135848990fcdc Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sun, 11 Feb 2024 20:17:05 +0000 Subject: [PATCH 70/89] Fixed predictive back gesture not working when started during normal transition --- .../predictiveback/PredictiveBackAnimation.kt | 168 +++++++++--------- 1 file changed, 87 insertions(+), 81 deletions(-) diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt index c30ff0303..94638e142 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.key import androidx.compose.runtime.movableContentOf @@ -20,8 +19,10 @@ import com.arkivanov.decompose.extensions.compose.stack.animation.LocalStackAnim import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation import com.arkivanov.decompose.extensions.compose.stack.animation.emptyStackAnimation import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.backhandler.BackEvent import com.arkivanov.essenty.backhandler.BackHandler +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch /** @@ -65,7 +66,8 @@ private class PredictiveBackAnimation( @Composable override fun invoke(stack: ChildStack, modifier: Modifier, content: @Composable (child: Child.Created) -> Unit) { - var activeConfigurations: Set by remember { mutableStateOf(emptySet()) } + val activeConfigurations = remember { HashSet() } + val handler = rememberHandler(stack = stack, isGestureEnabled = { activeConfigurations.size == 1 }) val animationProvider = LocalStackAnimationProvider.current val fallBackAnimation = animation ?: remember(animationProvider, animationProvider::provide) ?: emptyStackAnimation() @@ -83,24 +85,8 @@ private class PredictiveBackAnimation( } } - var data: Data by rememberMutableStateWithLatest(key = stack) { latestData -> - Data(stack = stack, key = latestData?.nextKey ?: 0) - } - - val (dataStack, dataKey, dataAnimatable) = data - - val items = - if (dataAnimatable == null) { - listOf(Item(stack = dataStack, key = dataKey, modifier = Modifier)) - } else { - listOf( - Item(stack = dataStack.dropLast(), key = dataKey + 1, modifier = dataAnimatable.enterModifier), - Item(stack = dataStack, key = dataKey, modifier = dataAnimatable.exitModifier), - ) - } - Box(modifier = modifier) { - items.forEach { item -> + handler.items.forEach { item -> key(item.key) { fallBackAnimation( stack = item.stack, @@ -111,75 +97,38 @@ private class PredictiveBackAnimation( } } - val isBackEnabled = dataStack.backStack.isNotEmpty() - val isBackGestureEnabled = isBackEnabled && ((dataAnimatable != null) || (activeConfigurations.size == 1)) - - if (isBackEnabled) { - if (isBackGestureEnabled) { - val scope = rememberCoroutineScope() - val initialBackEventRef = remember { Ref(null) } - - BackGestureHandler( - backHandler = backHandler, - onBackStarted = { initialBackEventRef.value = it }, - onBackProgressed = { - initialBackEventRef.value?.also { initialBackEvent -> - data = data.copy(animatable = selector(initialBackEvent, data.stack.active, data.stack.backStack.last())) - initialBackEventRef.value = null - } - - scope.launch { data.animatable?.animate(it) } - }, - onBackCancelled = { - initialBackEventRef.value = null - - scope.launch { - data.animatable?.cancel() - data = data.copy(animatable = null) - } - }, - onBack = { - initialBackEventRef.value = null - - if (data.animatable == null) { - onBack() - } else { - scope.launch { - data.animatable?.finish() - if (data.animatable != null) { - onBack() - } - } - } - } - ) - } else { - BackGestureHandler(backHandler = backHandler, onBack = onBack) + if (stack.backStack.isNotEmpty()) { + DisposableEffect(handler) { + backHandler.register(handler) + onDispose { backHandler.unregister(handler) } } } } + @Composable - private fun rememberMutableStateWithLatest( - key: Any, - getValue: (latestValue: T?) -> T, - ): MutableState { - val latestValue: Holder = remember { Holder(value = null) } - val state = remember(key) { mutableStateOf(getValue(latestValue.value)) } - latestValue.value = state.value - - return state + private fun rememberHandler(stack: ChildStack, isGestureEnabled: () -> Boolean): Handler { + val scope = key(stack) { rememberCoroutineScope() } + + return rememberWithLatest(stack) { previousHandler -> + Handler( + stack = stack, + scope = scope, + isGestureEnabled = isGestureEnabled, + key = previousHandler?.items?.maxOf { it.key } ?: 0, + selector = selector, + onBack = onBack, + ) + } } - private fun ChildStack.dropLast(): ChildStack = - ChildStack(active = backStack.last(), backStack = backStack.dropLast(1)) + @Composable + private fun rememberWithLatest(key: Any, supplier: (T?) -> T): T { + val ref = remember { Ref(null) } + val v = remember(key) { supplier(ref.value) } + ref.value = v - private data class Data( - val stack: ChildStack, - val key: Int, - val animatable: PredictiveBackAnimatable? = null, - ) { - val nextKey: Int get() = if (animatable == null) key else key + 1 + return v } private data class Item( @@ -188,5 +137,62 @@ private class PredictiveBackAnimation( val modifier: Modifier, ) - private class Holder(var value: T) + private class Handler( + private val stack: ChildStack, + private val scope: CoroutineScope, + private val isGestureEnabled: () -> Boolean, + private val key: Int, + private val selector: (BackEvent, exitChild: Child.Created, enterChild: Child.Created) -> PredictiveBackAnimatable, + private val onBack: () -> Unit, + ) : BackCallback() { + var items: List> by mutableStateOf(listOf(Item(stack = stack, key = key, modifier = Modifier))) + private set + + private var animatable: PredictiveBackAnimatable? = null + private var initialBackEvent: BackEvent? = null + + override fun onBackStarted(backEvent: BackEvent) { + initialBackEvent = backEvent + } + + override fun onBackProgressed(backEvent: BackEvent) { + val initialBackEvent = initialBackEvent + if ((initialBackEvent != null) && isGestureEnabled()) { + val animatable = selector(initialBackEvent, stack.active, stack.backStack.last()) + this.animatable = animatable + this.initialBackEvent = null + + items = + listOf( + Item(stack = stack.dropLast(), key = key + 1, modifier = animatable.enterModifier), + Item(stack = stack, key = key, modifier = animatable.exitModifier), + ) + } + + scope.launch { animatable?.animate(backEvent) } + } + + private fun ChildStack.dropLast(): ChildStack = + ChildStack(active = backStack.last(), backStack = backStack.dropLast(1)) + + override fun onBack() { + if (animatable == null) { + onBack.invoke() + } else { + scope.launch { + animatable?.finish() + animatable = null + onBack.invoke() + } + } + } + + override fun onBackCancelled() { + scope.launch { + animatable?.cancel() + animatable = null + items = listOf(Item(stack = stack, key = key, modifier = Modifier)) + } + } + } } From a991b0cb2099886fbb7870cd45acd45b20c96688 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Tue, 20 Feb 2024 20:55:27 +0000 Subject: [PATCH 71/89] Bumped version to 3.0.0-alpha07 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 09c48f293..d71d4fa10 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "3.0.0-alpha06" +decompose = "3.0.0-alpha07" kotlin = "1.9.22" essenty = "2.0.0-alpha07" reaktive = "1.2.3" From 0f31dfc8feb6d3d79c432a9f32bb548d1c62602b Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 22 Feb 2024 22:20:27 +0000 Subject: [PATCH 72/89] Move main thread check to children --- .../commonMain/kotlin/com/arkivanov/decompose/Relay.kt | 10 +--------- .../decompose/router/children/ChildrenFactory.kt | 3 +++ .../decompose/router/children/SimpleNavigation.kt | 2 +- .../decompose/router/slot/DefaultSlotNavigation.kt | 2 +- .../decompose/router/stack/DefaultStackNavigation.kt | 2 +- 5 files changed, 7 insertions(+), 12 deletions(-) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Relay.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Relay.kt index dad154d6b..2e9e9e2f5 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Relay.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Relay.kt @@ -1,10 +1,6 @@ package com.arkivanov.decompose -import com.arkivanov.decompose.mainthread.checkMainThread - -internal class Relay( - private val isMainThreadCheckEnabled: Boolean = false, -) { +internal class Relay { private val lock = Lock() private val queue = ArrayDeque() @@ -12,10 +8,6 @@ internal class Relay( private var observers = emptySet<(T) -> Unit>() fun accept(value: T) { - if (isMainThreadCheckEnabled) { - checkMainThread() - } - lock.synchronized { queue.addLast(value) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt index f02733b85..a6ce01be6 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt @@ -4,6 +4,7 @@ import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.Relay import com.arkivanov.decompose.backhandler.child +import com.arkivanov.decompose.mainthread.checkMainThread import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value import com.arkivanov.essenty.backhandler.BackCallback @@ -153,6 +154,8 @@ private class EventProcessor { private var holder: Holder<*, *, E, *, *>? = null fun process(event: NavEvent) { + checkMainThread() + when (event) { is NavEvent.Event -> { if (holder != null) { diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleNavigation.kt index 6270f2660..1a554da00 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleNavigation.kt @@ -9,7 +9,7 @@ import com.arkivanov.decompose.Relay */ class SimpleNavigation : NavigationSource { - private val relay = Relay(isMainThreadCheckEnabled = true) + private val relay = Relay() override fun subscribe(observer: (T) -> Unit): Cancellation = relay.subscribe(observer) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt index 599217272..88ffe35be 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/DefaultSlotNavigation.kt @@ -6,7 +6,7 @@ import com.arkivanov.decompose.router.slot.SlotNavigation.Event internal class DefaultSlotNavigation : SlotNavigation { - private val relay = Relay>(isMainThreadCheckEnabled = true) + private val relay = Relay>() override fun navigate( transformer: (configuration: C?) -> C?, diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt index b1c797584..e0ae9356a 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/DefaultStackNavigation.kt @@ -6,7 +6,7 @@ import com.arkivanov.decompose.router.stack.StackNavigation.Event internal class DefaultStackNavigation : StackNavigation { - private val relay = Relay>(isMainThreadCheckEnabled = true) + private val relay = Relay>() override fun navigate(transformer: (stack: List) -> List, onComplete: (newStack: List, oldStack: List) -> Unit) { relay.accept(Event(transformer, onComplete)) From a70f680425e32d8c03aac7f32def966ae529d2d4 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 1 Mar 2024 10:53:30 +0000 Subject: [PATCH 73/89] Fixed predictive back animation not working sometimes --- .../predictiveback/PredictiveBackAnimation.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt index 94638e142..b8e68e3a9 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackAnimation.kt @@ -69,7 +69,7 @@ private class PredictiveBackAnimation( val activeConfigurations = remember { HashSet() } val handler = rememberHandler(stack = stack, isGestureEnabled = { activeConfigurations.size == 1 }) val animationProvider = LocalStackAnimationProvider.current - val fallBackAnimation = animation ?: remember(animationProvider, animationProvider::provide) ?: emptyStackAnimation() + val anim = animation ?: remember(animationProvider, animationProvider::provide) ?: emptyStackAnimation() val childContent = remember(content) { @@ -88,9 +88,9 @@ private class PredictiveBackAnimation( Box(modifier = modifier) { handler.items.forEach { item -> key(item.key) { - fallBackAnimation( + anim( stack = item.stack, - modifier = Modifier.fillMaxSize().then(item.modifier), + modifier = Modifier.fillMaxSize().then(item.modifier()), content = childContent, ) } @@ -134,7 +134,7 @@ private class PredictiveBackAnimation( private data class Item( val stack: ChildStack, val key: Int, - val modifier: Modifier, + val modifier: () -> Modifier = { Modifier }, ) private class Handler( @@ -145,7 +145,7 @@ private class PredictiveBackAnimation( private val selector: (BackEvent, exitChild: Child.Created, enterChild: Child.Created) -> PredictiveBackAnimatable, private val onBack: () -> Unit, ) : BackCallback() { - var items: List> by mutableStateOf(listOf(Item(stack = stack, key = key, modifier = Modifier))) + var items: List> by mutableStateOf(listOf(Item(stack = stack, key = key))) private set private var animatable: PredictiveBackAnimatable? = null @@ -164,8 +164,8 @@ private class PredictiveBackAnimation( items = listOf( - Item(stack = stack.dropLast(), key = key + 1, modifier = animatable.enterModifier), - Item(stack = stack, key = key, modifier = animatable.exitModifier), + Item(stack = stack.dropLast(), key = key + 1, modifier = animatable::enterModifier), + Item(stack = stack, key = key, modifier = animatable::exitModifier), ) } @@ -191,7 +191,7 @@ private class PredictiveBackAnimation( scope.launch { animatable?.cancel() animatable = null - items = listOf(Item(stack = stack, key = key, modifier = Modifier)) + items = listOf(Item(stack = stack, key = key)) } } } From b9a9729145708af2a77b8bec14ec22eafe92dd13 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 29 Feb 2024 21:42:22 +0000 Subject: [PATCH 74/89] Fixed screen corners blinking when the predictive back gesture starts --- .../compose/stack/animation/predictiveback/LayoutCorners.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt index 1dd24b939..b8a6fbf75 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.kt @@ -16,7 +16,7 @@ internal data class LayoutCorners( internal data class LayoutCorner( val radius: Dp = 16.dp, - val isFixed: Boolean = true, + val isFixed: Boolean = false, ) internal fun LayoutCorners.toShape(progress: Float): RoundedCornerShape = From 38fbf305f78cad7055f2b993e32d8f5b0cb0421a Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 1 Mar 2024 13:54:08 +0000 Subject: [PATCH 75/89] Fixed screen corner animations in AndroidPredictiveBackAnimatable --- .../predictiveback/AndroidPredictiveBackAnimatable.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt index 6c8817f10..4f0762810 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt @@ -24,8 +24,10 @@ import kotlinx.coroutines.launch * @param initialBackEvent an initial [BackEvent] of the predictive back gesture. * @param exitShape an optional clipping shape of the child being removed (the currently active child). * If not supplied then a [RoundedCornerShape][androidx.compose.foundation.shape.RoundedCornerShape] will be applied. + * The `progress` argument is animating from 0 to 1 when the gesture is confirmed. * @param enterShape an optional clipping shape of the child being shown (the previous child). * If not supplied then a [RoundedCornerShape][androidx.compose.foundation.shape.RoundedCornerShape] will be applied. + * The `progress` argument is animating between 0 and 1 while the gesture is being performed. */ @ExperimentalDecomposeApi fun androidPredictiveBackAnimatable( @@ -73,7 +75,7 @@ private class AndroidPredictiveBackAnimatable( if (enterShape == null) { Modifier.withLayoutCorners { corners -> graphicsLayer { - setupEnterGraphicLayer { progress, _ -> corners.toShape(progress) } + setupEnterGraphicLayer { progress, _ -> corners.toShape(1F - progress) } } } } else { @@ -97,7 +99,7 @@ private class AndroidPredictiveBackAnimatable( scaleX = lerp(start = 0.95F, stop = 0.90F, fraction = enterProgress).plusFinishProgress(enterFinishProgress) scaleY = scaleX translationX = lerp(start = -size.width * 0.15F, stop = 0F, fraction = totalProgress) - shape = layoutShape(totalProgress, edge) + shape = layoutShape(enterFinishProgress, edge) clip = true } From 191ef92b5b614c499e98948e6541851f8dcc089e Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 1 Mar 2024 15:49:56 +0000 Subject: [PATCH 76/89] Refactored predictive back animatables --- .../AndroidPredictiveBackAnimatable.kt | 17 +++++++---------- .../MaterialPredictiveBackAnimatable.kt | 15 +-------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt index 4f0762810..6e6f0c4f3 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/AndroidPredictiveBackAnimatable.kt @@ -52,8 +52,8 @@ private class AndroidPredictiveBackAnimatable( private val exitProgress: Float by derivedStateOf { exitProgressAnimatable.value } private val enterProgressAnimatable = Animatable(initialValue = initialEvent.progress.enterProgress()) private val enterProgress: Float by derivedStateOf { enterProgressAnimatable.value } - private val enterFinishProgressAnimatable = Animatable(initialValue = 0F) - private val enterFinishProgress: Float by derivedStateOf { enterFinishProgressAnimatable.value } + private val finishProgressAnimatable = Animatable(initialValue = 0F) + private val finishProgress: Float by derivedStateOf { finishProgressAnimatable.value } private var edge by mutableStateOf(initialEvent.swipeEdge) override val exitModifier: Modifier @@ -94,18 +94,15 @@ private class AndroidPredictiveBackAnimatable( } private fun GraphicsLayerScope.setupEnterGraphicLayer(layoutShape: (progress: Float, edge: BackEvent.SwipeEdge) -> Shape) { - val totalProgress = enterProgress.plusFinishProgress(enterFinishProgress) + val totalProgress = lerp(start = enterProgress, stop = 1F, fraction = finishProgress) alpha = totalProgress - scaleX = lerp(start = 0.95F, stop = 0.90F, fraction = enterProgress).plusFinishProgress(enterFinishProgress) + scaleX = lerp(start = lerp(start = 0.95F, stop = 0.90F, fraction = enterProgress), stop = 1F, fraction = finishProgress) scaleY = scaleX translationX = lerp(start = -size.width * 0.15F, stop = 0F, fraction = totalProgress) - shape = layoutShape(enterFinishProgress, edge) + shape = layoutShape(finishProgress, edge) clip = true } - private fun Float.plusFinishProgress(progress: Float): Float = - (this + (1F - this) * progress).coerceIn(0F, 1F) - override suspend fun animate(event: BackEvent) { edge = event.swipeEdge @@ -118,7 +115,7 @@ private class AndroidPredictiveBackAnimatable( override suspend fun finish() { awaitAll( { exitProgressAnimatable.animateTo(targetValue = 1F) }, - { enterFinishProgressAnimatable.animateTo(targetValue = 1F) }, + { finishProgressAnimatable.animateTo(targetValue = 1F) }, ) } @@ -142,7 +139,7 @@ private class AndroidPredictiveBackAnimatable( if (this < PROGRESS_THRESHOLD) { 0F } else { - 1F - 0.6F * (1F - this) / (1F - PROGRESS_THRESHOLD) + lerp(start = 0.4F, stop = 1F, fraction = (this - PROGRESS_THRESHOLD) / (1F - PROGRESS_THRESHOLD)) } private companion object { diff --git a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt index 7681fc273..61bf510f1 100644 --- a/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt +++ b/extensions-compose/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/MaterialPredictiveBackAnimatable.kt @@ -1,7 +1,6 @@ package com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback import androidx.compose.animation.core.Animatable -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf @@ -14,7 +13,6 @@ import androidx.compose.ui.graphics.GraphicsLayerScope import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.essenty.backhandler.BackEvent @@ -57,7 +55,7 @@ private class MaterialPredictiveBackAnimatable( get() = if (exitShape == null) { Modifier.withLayoutCorners { corners -> - graphicsLayer { setupExitGraphicLayer(corners.toShape()) } + graphicsLayer { setupExitGraphicLayer(corners.toShape(progress)) } } } else { Modifier.graphicsLayer { @@ -104,17 +102,6 @@ private class MaterialPredictiveBackAnimatable( clip = true } - private fun LayoutCorners.toShape(): RoundedCornerShape = - RoundedCornerShape( - topStart = topStart.getProgressRadius(), - topEnd = topEnd.getProgressRadius(), - bottomEnd = bottomEnd.getProgressRadius(), - bottomStart = bottomStart.getProgressRadius(), - ) - - private fun LayoutCorner.getProgressRadius(): Dp = - if (isFixed) radius else radius * progress - override suspend fun animate(event: BackEvent) { edge = event.swipeEdge touchY = event.touchY From 4a59dc5227fe8db8a860adf097795b21d07c8577 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 2 Mar 2024 14:22:49 +0000 Subject: [PATCH 77/89] Bumped version to 3.0.0-alpha08 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index d71d4fa10..2b20b4103 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "3.0.0-alpha07" +decompose = "3.0.0-alpha08" kotlin = "1.9.22" essenty = "2.0.0-alpha07" reaktive = "1.2.3" From 3b8c27ad532ffa079e0428e7ac5586a8a086cc1f Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 9 Mar 2024 14:36:58 +0000 Subject: [PATCH 78/89] Monitor window focused state in desktop LifecycleController --- .../api/jvm/extensions-compose.api | 2 +- .../compose/lifecycle/LifecycleController.kt | 42 +++++++++++++++---- .../kotlin/com/arkivanov/sample/app/Main.kt | 10 +++-- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/extensions-compose/api/jvm/extensions-compose.api b/extensions-compose/api/jvm/extensions-compose.api index 001c819bb..2cb6b70c1 100644 --- a/extensions-compose/api/jvm/extensions-compose.api +++ b/extensions-compose/api/jvm/extensions-compose.api @@ -3,7 +3,7 @@ public final class com/arkivanov/decompose/extensions/compose/SubscribeAsStateKt } public final class com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleControllerKt { - public static final fun LifecycleController (Lcom/arkivanov/essenty/lifecycle/LifecycleRegistry;Landroidx/compose/ui/window/WindowState;Landroidx/compose/runtime/Composer;I)V + public static final fun LifecycleController (Lcom/arkivanov/essenty/lifecycle/LifecycleRegistry;Landroidx/compose/ui/window/WindowState;Landroidx/compose/ui/platform/WindowInfo;Landroidx/compose/runtime/Composer;II)V } public final class com/arkivanov/decompose/extensions/compose/pages/ComposableSingletons$PagesKt { diff --git a/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt b/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt index 35f56f8e3..0c0da4803 100644 --- a/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt +++ b/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt @@ -4,24 +4,50 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.platform.WindowInfo import androidx.compose.ui.window.WindowState import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.create import com.arkivanov.essenty.lifecycle.destroy +import com.arkivanov.essenty.lifecycle.pause import com.arkivanov.essenty.lifecycle.resume +import com.arkivanov.essenty.lifecycle.start import com.arkivanov.essenty.lifecycle.stop -import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.combine +/** + * Controls (drives) the provided [LifecycleRegistry] according to the window minimized and + * focused state changes. + * The lifecycle state is [Lifecycle.State.RESUMED] when the window is focused. + * The lifecycle state is [Lifecycle.State.CREATED] when the window is minimized. + * The lifecycle state is [Lifecycle.State.STARTED] otherwise. + * The [LifecycleRegistry] is destroyed when this function leaves the composition. + * + * @param lifecycleRegistry a [LifecycleRegistry] that should be controlled. + * @param windowState a [WindowState] providing the window minimized state, see [WindowState.isMinimized]. + * @param windowInfo an optional [WindowInfo] providing the window focused state, see [WindowInfo.isWindowFocused]. + * If not provided, the window is always considered focused. + */ @ExperimentalDecomposeApi @Composable -fun LifecycleController(lifecycleRegistry: LifecycleRegistry, windowState: WindowState) { - LaunchedEffect(lifecycleRegistry, windowState) { - snapshotFlow(windowState::isMinimized).collect { isMinimized -> - if (isMinimized) { - lifecycleRegistry.stop() - } else { - lifecycleRegistry.resume() +fun LifecycleController( + lifecycleRegistry: LifecycleRegistry, + windowState: WindowState, + windowInfo: WindowInfo? = null, +) { + LaunchedEffect(lifecycleRegistry, windowState, windowInfo) { + combine( + snapshotFlow(windowState::isMinimized), + snapshotFlow { windowInfo?.isWindowFocused ?: true }, + ::Pair, + ).collect { (isMinimized, isFocused) -> + when { + isMinimized -> lifecycleRegistry.stop() + isFocused -> lifecycleRegistry.resume() + lifecycleRegistry.state == Lifecycle.State.RESUMED -> lifecycleRegistry.pause() + else -> lifecycleRegistry.start() } } } diff --git a/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt b/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt index 48ffaecf2..290779f1b 100644 --- a/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt +++ b/sample/app-desktop/src/jvmMain/kotlin/com/arkivanov/sample/app/Main.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalWindowInfo import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application @@ -57,9 +58,6 @@ fun main() { application { val windowState = rememberWindowState() - - LifecycleController(lifecycle, windowState) - var isCloseRequested by remember { mutableStateOf(false) } Window( @@ -67,6 +65,12 @@ fun main() { state = windowState, title = "Decompose Sample" ) { + LifecycleController( + lifecycleRegistry = lifecycle, + windowState = windowState, + windowInfo = LocalWindowInfo.current, + ) + RootContent(root) if (isCloseRequested) { From d8c7d37a03e525ae46cba70f9e4972a1508dec0f Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Mon, 4 Mar 2024 01:12:11 +0000 Subject: [PATCH 79/89] Improved component context API for better custom component contexts --- decompose/api/android/decompose.api | 54 +++++++++++-------- decompose/api/jvm/decompose.api | 54 +++++++++++-------- .../arkivanov/decompose/ComponentContext.kt | 20 +++---- .../decompose/ComponentContextExt.kt | 10 ++-- .../decompose/ComponentContextFactory.kt | 24 +++++++++ .../decompose/ComponentContextFactoryOwner.kt | 18 +++++++ .../decompose/DefaultComponentContext.kt | 5 ++ .../decompose/GenericComponentContext.kt | 18 +++++++ .../router/children/ChildrenFactory.kt | 15 +++--- .../children/DefaultChildItemFactory.kt | 10 ++-- .../router/pages/ChildPagesFactory.kt | 9 ++-- .../decompose/router/slot/ChildSlotFactory.kt | 10 ++-- .../router/stack/ChildStackFactory.kt | 13 ++--- .../ChildContextWithLifecycleTest.kt | 1 + .../ChildContextWithoutLifecycleTest.kt | 1 + .../counter/DefaultCounterComponent.kt | 2 +- 16 files changed, 179 insertions(+), 85 deletions(-) create mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactory.kt create mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactoryOwner.kt create mode 100644 decompose/src/commonMain/kotlin/com/arkivanov/decompose/GenericComponentContext.kt diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 3db91a65e..897d7e491 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -33,12 +33,20 @@ public final class com/arkivanov/decompose/Child$Destroyed : com/arkivanov/decom public fun toString ()Ljava/lang/String; } -public abstract interface class com/arkivanov/decompose/ComponentContext : com/arkivanov/essenty/backhandler/BackHandlerOwner, com/arkivanov/essenty/instancekeeper/InstanceKeeperOwner, com/arkivanov/essenty/lifecycle/LifecycleOwner, com/arkivanov/essenty/statekeeper/StateKeeperOwner { +public abstract interface class com/arkivanov/decompose/ComponentContext : com/arkivanov/decompose/GenericComponentContext { } public final class com/arkivanov/decompose/ComponentContextExtKt { - public static final fun childContext (Lcom/arkivanov/decompose/ComponentContext;Ljava/lang/String;Lcom/arkivanov/essenty/lifecycle/Lifecycle;)Lcom/arkivanov/decompose/ComponentContext; - public static synthetic fun childContext$default (Lcom/arkivanov/decompose/ComponentContext;Ljava/lang/String;Lcom/arkivanov/essenty/lifecycle/Lifecycle;ILjava/lang/Object;)Lcom/arkivanov/decompose/ComponentContext; + public static final fun childContext (Lcom/arkivanov/decompose/GenericComponentContext;Ljava/lang/String;Lcom/arkivanov/essenty/lifecycle/Lifecycle;)Lcom/arkivanov/decompose/GenericComponentContext; + public static synthetic fun childContext$default (Lcom/arkivanov/decompose/GenericComponentContext;Ljava/lang/String;Lcom/arkivanov/essenty/lifecycle/Lifecycle;ILjava/lang/Object;)Lcom/arkivanov/decompose/GenericComponentContext; +} + +public abstract interface class com/arkivanov/decompose/ComponentContextFactory { + public abstract fun invoke (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;)Ljava/lang/Object; +} + +public abstract interface class com/arkivanov/decompose/ComponentContextFactoryOwner { + public abstract fun getComponentContextFactory ()Lcom/arkivanov/decompose/ComponentContextFactory; } public final class com/arkivanov/decompose/DefaultComponentContext : com/arkivanov/decompose/ComponentContext { @@ -46,6 +54,7 @@ public final class com/arkivanov/decompose/DefaultComponentContext : com/arkivan public fun (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;)V public synthetic fun (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getBackHandler ()Lcom/arkivanov/essenty/backhandler/BackHandler; + public fun getComponentContextFactory ()Lcom/arkivanov/decompose/ComponentContextFactory; public fun getInstanceKeeper ()Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper; public fun getLifecycle ()Lcom/arkivanov/essenty/lifecycle/Lifecycle; public fun getStateKeeper ()Lcom/arkivanov/essenty/statekeeper/StateKeeper; @@ -66,6 +75,9 @@ public abstract interface annotation class com/arkivanov/decompose/ExperimentalD public abstract interface annotation class com/arkivanov/decompose/FaultyDecomposeApi : java/lang/annotation/Annotation { } +public abstract interface class com/arkivanov/decompose/GenericComponentContext : com/arkivanov/decompose/ComponentContextFactoryOwner, com/arkivanov/essenty/backhandler/BackHandlerOwner, com/arkivanov/essenty/instancekeeper/InstanceKeeperOwner, com/arkivanov/essenty/lifecycle/LifecycleOwner, com/arkivanov/essenty/statekeeper/StateKeeperOwner { +} + public abstract interface annotation class com/arkivanov/decompose/InternalDecomposeApi : java/lang/annotation/Annotation { } @@ -100,10 +112,10 @@ public final class com/arkivanov/decompose/router/children/ChildNavState$Status } public final class com/arkivanov/decompose/router/children/ChildrenFactoryKt { - public static final fun children (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun children (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun children$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun children$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun children (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun children (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun children$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun children$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } public abstract interface class com/arkivanov/decompose/router/children/NavState { @@ -148,10 +160,10 @@ public final class com/arkivanov/decompose/router/pages/ChildPages { } public final class com/arkivanov/decompose/router/pages/ChildPagesFactoryKt { - public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun childPages (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childPages (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childPages$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childPages$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static final fun getDefaultPageStatus (ILcom/arkivanov/decompose/router/pages/Pages;)Lcom/arkivanov/decompose/router/children/ChildNavState$Status; } @@ -217,10 +229,10 @@ public final class com/arkivanov/decompose/router/slot/ChildSlot { } public final class com/arkivanov/decompose/router/slot/ChildSlotFactoryKt { - public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun childSlot (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childSlot (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigation : com/arkivanov/decompose/router/children/NavigationSource, com/arkivanov/decompose/router/slot/SlotNavigator { @@ -270,12 +282,12 @@ public final class com/arkivanov/decompose/router/stack/ChildStack { } public final class com/arkivanov/decompose/router/stack/ChildStackFactoryKt { - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } public abstract interface class com/arkivanov/decompose/router/stack/StackNavigation : com/arkivanov/decompose/router/children/NavigationSource, com/arkivanov/decompose/router/stack/StackNavigator { diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api index f77831103..846f385ac 100644 --- a/decompose/api/jvm/decompose.api +++ b/decompose/api/jvm/decompose.api @@ -33,12 +33,20 @@ public final class com/arkivanov/decompose/Child$Destroyed : com/arkivanov/decom public fun toString ()Ljava/lang/String; } -public abstract interface class com/arkivanov/decompose/ComponentContext : com/arkivanov/essenty/backhandler/BackHandlerOwner, com/arkivanov/essenty/instancekeeper/InstanceKeeperOwner, com/arkivanov/essenty/lifecycle/LifecycleOwner, com/arkivanov/essenty/statekeeper/StateKeeperOwner { +public abstract interface class com/arkivanov/decompose/ComponentContext : com/arkivanov/decompose/GenericComponentContext { } public final class com/arkivanov/decompose/ComponentContextExtKt { - public static final fun childContext (Lcom/arkivanov/decompose/ComponentContext;Ljava/lang/String;Lcom/arkivanov/essenty/lifecycle/Lifecycle;)Lcom/arkivanov/decompose/ComponentContext; - public static synthetic fun childContext$default (Lcom/arkivanov/decompose/ComponentContext;Ljava/lang/String;Lcom/arkivanov/essenty/lifecycle/Lifecycle;ILjava/lang/Object;)Lcom/arkivanov/decompose/ComponentContext; + public static final fun childContext (Lcom/arkivanov/decompose/GenericComponentContext;Ljava/lang/String;Lcom/arkivanov/essenty/lifecycle/Lifecycle;)Lcom/arkivanov/decompose/GenericComponentContext; + public static synthetic fun childContext$default (Lcom/arkivanov/decompose/GenericComponentContext;Ljava/lang/String;Lcom/arkivanov/essenty/lifecycle/Lifecycle;ILjava/lang/Object;)Lcom/arkivanov/decompose/GenericComponentContext; +} + +public abstract interface class com/arkivanov/decompose/ComponentContextFactory { + public abstract fun invoke (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;)Ljava/lang/Object; +} + +public abstract interface class com/arkivanov/decompose/ComponentContextFactoryOwner { + public abstract fun getComponentContextFactory ()Lcom/arkivanov/decompose/ComponentContextFactory; } public final class com/arkivanov/decompose/DefaultComponentContext : com/arkivanov/decompose/ComponentContext { @@ -46,6 +54,7 @@ public final class com/arkivanov/decompose/DefaultComponentContext : com/arkivan public fun (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;)V public synthetic fun (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getBackHandler ()Lcom/arkivanov/essenty/backhandler/BackHandler; + public fun getComponentContextFactory ()Lcom/arkivanov/decompose/ComponentContextFactory; public fun getInstanceKeeper ()Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper; public fun getLifecycle ()Lcom/arkivanov/essenty/lifecycle/Lifecycle; public fun getStateKeeper ()Lcom/arkivanov/essenty/statekeeper/StateKeeper; @@ -57,6 +66,9 @@ public abstract interface annotation class com/arkivanov/decompose/ExperimentalD public abstract interface annotation class com/arkivanov/decompose/FaultyDecomposeApi : java/lang/annotation/Annotation { } +public abstract interface class com/arkivanov/decompose/GenericComponentContext : com/arkivanov/decompose/ComponentContextFactoryOwner, com/arkivanov/essenty/backhandler/BackHandlerOwner, com/arkivanov/essenty/instancekeeper/InstanceKeeperOwner, com/arkivanov/essenty/lifecycle/LifecycleOwner, com/arkivanov/essenty/statekeeper/StateKeeperOwner { +} + public abstract interface annotation class com/arkivanov/decompose/InternalDecomposeApi : java/lang/annotation/Annotation { } @@ -84,10 +96,10 @@ public final class com/arkivanov/decompose/router/children/ChildNavState$Status } public final class com/arkivanov/decompose/router/children/ChildrenFactoryKt { - public static final fun children (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun children (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun children$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun children$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun children (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun children (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun children$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun children$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } public abstract interface class com/arkivanov/decompose/router/children/NavState { @@ -132,10 +144,10 @@ public final class com/arkivanov/decompose/router/pages/ChildPages { } public final class com/arkivanov/decompose/router/pages/ChildPagesFactoryKt { - public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childPages (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childPages$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun childPages (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childPages (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childPages$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childPages$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; public static final fun getDefaultPageStatus (ILcom/arkivanov/decompose/router/pages/Pages;)Lcom/arkivanov/decompose/router/children/ChildNavState$Status; } @@ -201,10 +213,10 @@ public final class com/arkivanov/decompose/router/slot/ChildSlot { } public final class com/arkivanov/decompose/router/slot/ChildSlotFactoryKt { - public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childSlot (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun childSlot (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childSlot (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childSlot$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } public abstract interface class com/arkivanov/decompose/router/slot/SlotNavigation : com/arkivanov/decompose/router/children/NavigationSource, com/arkivanov/decompose/router/slot/SlotNavigator { @@ -254,12 +266,12 @@ public final class com/arkivanov/decompose/router/stack/ChildStack { } public final class com/arkivanov/decompose/router/stack/ChildStackFactoryKt { - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; - public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static final fun childStack (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; + public static synthetic fun childStack$default (Lcom/arkivanov/decompose/GenericComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function0;Ljava/lang/String;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value; } public abstract interface class com/arkivanov/decompose/router/stack/StackNavigation : com/arkivanov/decompose/router/children/NavigationSource, com/arkivanov/decompose/router/stack/StackNavigator { diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContext.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContext.kt index ff6e31dc4..ee6b6c186 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContext.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContext.kt @@ -1,12 +1,12 @@ package com.arkivanov.decompose -import com.arkivanov.essenty.backhandler.BackHandlerOwner -import com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner -import com.arkivanov.essenty.lifecycle.LifecycleOwner -import com.arkivanov.essenty.statekeeper.StateKeeperOwner - -interface ComponentContext : - LifecycleOwner, - StateKeeperOwner, - InstanceKeeperOwner, - BackHandlerOwner +/** + * A default component context interface provided by Decompose. + * Should be passed to component classes via their constructors to + * enable features like lifecycle handling, state preservation, navigation, + * etc. + * + * It is also possible to define your own interface with additional + * properties or methods, and use that instead of this one. + */ +interface ComponentContext : GenericComponentContext diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextExt.kt index b5735866b..0cd75d8fe 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextExt.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextExt.kt @@ -8,19 +8,19 @@ import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.doOnDestroy /** - * Creates a new instance of child [ComponentContext] and attaches it to the parent (`this`) [ComponentContext]. + * Creates a new instance of child component context of type [Ctx] and attaches it to the parent (`this`) component context. * - * @param key A key of the child [ComponentContext], must be unique within the parent [ComponentContext]. - * @param lifecycle An optional [Lifecycle] of the child [ComponentContext] to be merged with the parent [Lifecycle], + * @param key A key of the child component context, must be unique within the parent context. + * @param lifecycle An optional [Lifecycle] of the child component context to be merged with the parent [Lifecycle], * can be used for manual control (see [LifecycleRegistry][com.arkivanov.essenty.lifecycle.LifecycleRegistry]). * The following conditions apply: * - The resulting [Lifecycle] of the child component will honour both the parent (`this`) [Lifecycle] and the supplied one. * - The supplied [Lifecycle] must never be destroyed manually. */ -fun ComponentContext.childContext(key: String, lifecycle: Lifecycle? = null): ComponentContext { +fun > Ctx.childContext(key: String, lifecycle: Lifecycle? = null): Ctx { lifecycle?.doOnDestroy { error("The lifecycle of a child ComponentContext must never be destroyed manually.") } - return DefaultComponentContext( + return componentContextFactory( lifecycle = if (lifecycle == null) this.lifecycle else MergedLifecycle(this.lifecycle, lifecycle), stateKeeper = stateKeeper.child(key, lifecycle), instanceKeeper = instanceKeeper.child(key, lifecycle), diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactory.kt new file mode 100644 index 000000000..0519bdfe4 --- /dev/null +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactory.kt @@ -0,0 +1,24 @@ +package com.arkivanov.decompose + +import com.arkivanov.essenty.backhandler.BackHandler +import com.arkivanov.essenty.instancekeeper.InstanceKeeper +import com.arkivanov.essenty.lifecycle.Lifecycle +import com.arkivanov.essenty.statekeeper.StateKeeper + +/** + * Represents a factory that creates new instances of component contexts of type [T]. + * Used by various navigation models that require creating child component contexts. + */ +fun interface ComponentContextFactory { + + /** + * Creates a new instance of component context of type [T], not attached to any + * parent component context. + */ + operator fun invoke( + lifecycle: Lifecycle, + stateKeeper: StateKeeper, + instanceKeeper: InstanceKeeper, + backHandler: BackHandler, + ): T +} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactoryOwner.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactoryOwner.kt new file mode 100644 index 000000000..ab61b47bf --- /dev/null +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContextFactoryOwner.kt @@ -0,0 +1,18 @@ +package com.arkivanov.decompose + +/** + * Represents a holder of [ComponentContextFactory]. + */ +interface ComponentContextFactoryOwner { + + /** + * Returns a [ComponentContextFactory] that creates new instances of + * component context of type [T], not attached to any parent component context. + * + * This property is usually used by various navigation models (such as + * [childStack][com.arkivanov.decompose.router.stack.childStack]) for creating + * component contexts for child components. If you need to create a child + * component context, see [childContext]. + */ + val componentContextFactory: ComponentContextFactory +} diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/DefaultComponentContext.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/DefaultComponentContext.kt index c8aa6cc15..7a8bbcd4b 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/DefaultComponentContext.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/DefaultComponentContext.kt @@ -9,6 +9,10 @@ import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.statekeeper.StateKeeper import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher +/** + * A default implementation of the [ComponentContext] interface. + * Can be also used in tests. + */ class DefaultComponentContext( override val lifecycle: Lifecycle, stateKeeper: StateKeeper? = null, @@ -19,6 +23,7 @@ class DefaultComponentContext( override val stateKeeper: StateKeeper = stateKeeper ?: StateKeeperDispatcher() override val instanceKeeper: InstanceKeeper = instanceKeeper ?: InstanceKeeperDispatcher().attachTo(lifecycle) override val backHandler: BackHandler = backHandler ?: BackDispatcher() + override val componentContextFactory: ComponentContextFactory = ComponentContextFactory(::DefaultComponentContext) constructor(lifecycle: Lifecycle) : this( lifecycle = lifecycle, diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/GenericComponentContext.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/GenericComponentContext.kt new file mode 100644 index 000000000..4d923f74c --- /dev/null +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/GenericComponentContext.kt @@ -0,0 +1,18 @@ +package com.arkivanov.decompose + +import com.arkivanov.essenty.backhandler.BackHandlerOwner +import com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner +import com.arkivanov.essenty.lifecycle.LifecycleOwner +import com.arkivanov.essenty.statekeeper.StateKeeperOwner + +/** + * A generic component context that extends [LifecycleOwner], [StateKeeperOwner], + * [InstanceKeeperOwner] and [BackHandlerOwner] interfaces, and also able to create + * new instances of itself via [ComponentContextFactory]. + */ +interface GenericComponentContext : + LifecycleOwner, + StateKeeperOwner, + InstanceKeeperOwner, + BackHandlerOwner, + ComponentContextFactoryOwner diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt index a6ce01be6..f75926bae 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt @@ -1,7 +1,7 @@ package com.arkivanov.decompose.router.children import com.arkivanov.decompose.Child -import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.GenericComponentContext import com.arkivanov.decompose.Relay import com.arkivanov.decompose.backhandler.child import com.arkivanov.decompose.mainthread.checkMainThread @@ -20,7 +20,7 @@ import kotlinx.serialization.Serializable * so it's automatically saved and restored. This method can be used if the custom save/restore logic * is not required. */ -fun , S : Any> ComponentContext.children( +fun , C : Any, T : Any, E : Any, N : NavState, S : Any> Ctx.children( source: NavigationSource, stateSerializer: KSerializer?, initialState: () -> N, @@ -30,7 +30,7 @@ fun , S : Any> ComponentContext.child onStateChanged: (newState: N, oldState: N?) -> Unit = { _, _ -> }, onEventComplete: (event: E, newState: N, oldState: N) -> Unit = { _, _, _ -> }, backTransformer: (state: N) -> (() -> N)? = { null }, - childFactory: (configuration: C, componentContext: ComponentContext) -> T, + childFactory: (configuration: C, componentContext: Ctx) -> T, ): Value = children( source = source, @@ -102,7 +102,7 @@ fun , S : Any> ComponentContext.child * @param childFactory a factory function that creates new child component instances. * @return an observable [Value] of the resulting children state. */ -fun , S : Any> ComponentContext.children( +fun , C : Any, T : Any, E : Any, N : NavState, S : Any> Ctx.children( source: NavigationSource, key: String, initialState: () -> N, @@ -113,7 +113,7 @@ fun , S : Any> ComponentContext.child onStateChanged: (newState: N, oldState: N?) -> Unit = { _, _ -> }, onEventComplete: (event: E, newState: N, oldState: N) -> Unit = { _, _, _ -> }, backTransformer: (state: N) -> (() -> N)? = { null }, - childFactory: (configuration: C, componentContext: ComponentContext) -> T, + childFactory: (configuration: C, componentContext: Ctx) -> T, ): Value { val mainBackHandler = backHandler.child() val relay = Relay>() @@ -220,12 +220,12 @@ private class Holder, S : Any> } } -private fun > ComponentContext.childrenNavigator( +private fun , C : Any, T : Any, N : NavState> Ctx.childrenNavigator( key: String, initialState: () -> N, saveState: (state: N) -> SerializableContainer?, restoreState: (container: SerializableContainer) -> N?, - childFactory: (configuration: C, componentContext: ComponentContext) -> T, + childFactory: (configuration: C, componentContext: Ctx) -> T, ): ChildrenNavigator { val navigator = stateKeeper.consume(key = key, strategy = SavedState.serializer()).let { savedState -> @@ -235,6 +235,7 @@ private fun > ComponentContext.childrenNavigat lifecycle = lifecycle, retainedInstanceSupplier = { factory -> instanceKeeper.getOrCreate(key = key, factory = factory) }, childItemFactory = DefaultChildItemFactory( + contextFactory = componentContextFactory, lifecycle = lifecycle, backHandler = backHandler.child(priority = BackCallback.PRIORITY_DEFAULT + 1), childFactory = childFactory, diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt index b9b23df36..e9ec01b86 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt @@ -1,7 +1,6 @@ package com.arkivanov.decompose.router.children -import com.arkivanov.decompose.ComponentContext -import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.ComponentContextFactory import com.arkivanov.decompose.backhandler.childBackHandler import com.arkivanov.decompose.lifecycle.MergedLifecycle import com.arkivanov.essenty.backhandler.BackHandler @@ -11,10 +10,11 @@ import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.statekeeper.SerializableContainer import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher -internal class DefaultChildItemFactory( +internal class DefaultChildItemFactory( + private val contextFactory: ComponentContextFactory, private val lifecycle: Lifecycle, private val backHandler: BackHandler, - private val childFactory: (configuration: C, ComponentContext) -> T, + private val childFactory: (configuration: C, Ctx) -> T, ) : ChildItemFactory { override fun invoke( @@ -31,7 +31,7 @@ internal class DefaultChildItemFactory( val component = childFactory( configuration, - DefaultComponentContext( + contextFactory( lifecycle = mergedLifecycle, stateKeeper = stateKeeperDispatcher, instanceKeeper = instanceKeeperRegistry, diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt index 0aa9b1675..84fee183a 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/pages/ChildPagesFactory.kt @@ -2,6 +2,7 @@ package com.arkivanov.decompose.router.pages import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.decompose.GenericComponentContext import com.arkivanov.decompose.router.children.ChildNavState import com.arkivanov.decompose.router.children.ChildNavState.Status import com.arkivanov.decompose.router.children.NavState @@ -35,14 +36,14 @@ import kotlinx.serialization.Serializable * @return an observable [Value] of [ChildPages]. */ @ExperimentalDecomposeApi -fun ComponentContext.childPages( +fun , C : Any, T : Any> Ctx.childPages( source: NavigationSource>, serializer: KSerializer?, initialPages: () -> Pages = { Pages() }, key: String = "DefaultChildPages", pageStatus: (index: Int, Pages) -> Status = ::getDefaultPageStatus, handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, + childFactory: (configuration: C, Ctx) -> T, ): Value> = childPages( source = source, @@ -104,7 +105,7 @@ private class SerializablePages( * @return an observable [Value] of [ChildPages]. */ @ExperimentalDecomposeApi -fun ComponentContext.childPages( +fun , C : Any, T : Any> Ctx.childPages( source: NavigationSource>, initialPages: () -> Pages, savePages: (Pages) -> SerializableContainer?, @@ -112,7 +113,7 @@ fun ComponentContext.childPages( key: String = "DefaultChildPages", pageStatus: (index: Int, Pages) -> Status = ::getDefaultPageStatus, handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, + childFactory: (configuration: C, Ctx) -> T, ): Value> = children( source = source, diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt index 5e8564359..102d6331e 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/slot/ChildSlotFactory.kt @@ -1,7 +1,7 @@ package com.arkivanov.decompose.router.slot import com.arkivanov.decompose.Child -import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.GenericComponentContext import com.arkivanov.decompose.router.children.ChildNavState.Status import com.arkivanov.decompose.router.children.NavState import com.arkivanov.decompose.router.children.NavigationSource @@ -27,13 +27,13 @@ import kotlinx.serialization.KSerializer * @param childFactory a factory function that creates new child instances. * @return an observable [Value] of [ChildSlot]. */ -fun ComponentContext.childSlot( +fun , C : Any, T : Any> Ctx.childSlot( source: NavigationSource>, serializer: KSerializer?, initialConfiguration: () -> C? = { null }, key: String = "DefaultChildSlot", handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, + childFactory: (configuration: C, Ctx) -> T, ): Value> = childSlot( source = source, @@ -72,14 +72,14 @@ fun ComponentContext.childSlot( * @param childFactory a factory function that creates new child instances. * @return an observable [Value] of [ChildSlot]. */ -fun ComponentContext.childSlot( +fun , C : Any, T : Any> Ctx.childSlot( source: NavigationSource>, saveConfiguration: (C?) -> SerializableContainer?, restoreConfiguration: (SerializableContainer) -> C?, key: String = "DefaultChildSlot", initialConfiguration: () -> C? = { null }, handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, + childFactory: (configuration: C, Ctx) -> T, ): Value> = children( source = source, diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt index a5a5e50c5..9aa9ae179 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt @@ -2,6 +2,7 @@ package com.arkivanov.decompose.router.stack import com.arkivanov.decompose.Child import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.GenericComponentContext import com.arkivanov.decompose.router.children.ChildNavState.Status import com.arkivanov.decompose.router.children.NavState import com.arkivanov.decompose.router.children.NavigationSource @@ -27,13 +28,13 @@ import kotlinx.serialization.builtins.ListSerializer * @param childFactory a factory function that creates new child instances. * @return an observable [Value] of [ChildStack]. */ -fun ComponentContext.childStack( +fun , C : Any, T : Any> Ctx.childStack( source: NavigationSource>, serializer: KSerializer?, initialStack: () -> List, key: String = "DefaultChildStack", handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, + childFactory: (configuration: C, Ctx) -> T, ): Value> = childStack( source = source, @@ -60,13 +61,13 @@ fun ComponentContext.childStack( /** * A convenience extension function for [ComponentContext.childStack]. */ -fun ComponentContext.childStack( +fun , C : Any, T : Any> Ctx.childStack( source: NavigationSource>, serializer: KSerializer?, initialConfiguration: C, key: String = "DefaultChildStack", handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T + childFactory: (configuration: C, Ctx) -> T ): Value> = childStack( source = source, @@ -94,14 +95,14 @@ fun ComponentContext.childStack( * @param childFactory a factory function that creates new child instances. * @return an observable [Value] of [ChildStack]. */ -fun ComponentContext.childStack( +fun , C : Any, T : Any> Ctx.childStack( source: NavigationSource>, initialStack: () -> List, saveStack: (List) -> SerializableContainer?, restoreStack: (SerializableContainer) -> List?, key: String = "DefaultChildStack", handleBackButton: Boolean = false, - childFactory: (configuration: C, ComponentContext) -> T, + childFactory: (configuration: C, Ctx) -> T, ): Value> = children( source = source, diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithLifecycleTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithLifecycleTest.kt index 101ec92d5..4655d74de 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithLifecycleTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithLifecycleTest.kt @@ -245,5 +245,6 @@ class ChildContextWithLifecycleTest { override val lifecycle: LifecycleRegistry = LifecycleRegistry() override val stateKeeper: TestStateKeeperDispatcher = TestStateKeeperDispatcher(savedState) override val backHandler: TestBackDispatcher = TestBackDispatcher() + override val componentContextFactory: ComponentContextFactory = ComponentContextFactory(::DefaultComponentContext) } } diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithoutLifecycleTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithoutLifecycleTest.kt index 690089c51..7fc2a70af 100644 --- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithoutLifecycleTest.kt +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/ChildContextWithoutLifecycleTest.kt @@ -156,5 +156,6 @@ class ChildContextWithoutLifecycleTest { override val lifecycle: LifecycleRegistry = LifecycleRegistry() override val stateKeeper: TestStateKeeperDispatcher = TestStateKeeperDispatcher(savedState) override val backHandler: BackDispatcher = BackDispatcher() + override val componentContextFactory: ComponentContextFactory = ComponentContextFactory(::DefaultComponentContext) } } diff --git a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/DefaultCounterComponent.kt b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/DefaultCounterComponent.kt index f3ef8ba34..b19067e87 100644 --- a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/DefaultCounterComponent.kt +++ b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/counters/counter/DefaultCounterComponent.kt @@ -43,7 +43,7 @@ internal class DefaultCounterComponent( private val dialogNavigation = SlotNavigation() private val _dialogSlot = - childSlot( + childSlot( source = dialogNavigation, serializer = null, handleBackButton = true, From 0f52b0b56af1c3bd22340f46de74b441c7ed590c Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 14 Mar 2024 20:24:11 +0000 Subject: [PATCH 80/89] Updated Kotlin to 1.9.23, Compose to 1.6.1 and other dependencies --- decompose/build.gradle.kts | 2 -- deps.versions.toml | 13 ++++--------- extensions-compose/build.gradle.kts | 4 ---- .../predictiveback/LayoutCorners.android.kt | 3 +-- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/decompose/build.gradle.kts b/decompose/build.gradle.kts index 1b8fb165d..85f4b7f26 100644 --- a/decompose/build.gradle.kts +++ b/decompose/build.gradle.kts @@ -64,9 +64,7 @@ kotlin { } android.main.dependencies { - implementation(deps.androidx.activity.activityKtx) implementation(deps.androidx.fragment.fragmentKtx) - implementation(deps.androidx.lifecycle.lifecycleCommonJava8) } android.test.dependencies { diff --git a/deps.versions.toml b/deps.versions.toml index 2b20b4103..a6d08e1cd 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,11 +1,11 @@ [versions] decompose = "3.0.0-alpha08" -kotlin = "1.9.22" +kotlin = "1.9.23" essenty = "2.0.0-alpha07" reaktive = "1.2.3" junit = "4.13.2" -jetbrainsCompose = "1.6.0-rc02" +jetbrainsCompose = "1.6.1" jetbrainsKotlinWrappers = "1.0.0-pre.608" jetbrainsKotlinxCoroutines = "1.8.0" jetbrainsKotlinxSerialization = "1.6.2" @@ -16,10 +16,8 @@ androidMaterial = "1.6.1" androidPlay = "1.10.3" androidxCore = "1.9.0" androidxAppcompat = "1.6.0" -androidxLifecycle = "2.5.1" -androidxActivity = "1.6.1" -androidxFragment = "1.1.0" -androidxTestCore = "1.5.0" +androidxActivity = "1.8.2" +androidxFragment = "1.6.2" [libraries] @@ -55,8 +53,5 @@ android-play-core = { group = "com.google.android.play", name = "core", version. androidx-core-coreKtx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" } androidx-appcompat-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppcompat" } -androidx-lifecycle-lifecycleCommonJava8 = { group = "androidx.lifecycle", name = "lifecycle-common-java8", version.ref = "androidxLifecycle" } -androidx-activity-activityKtx = { group = "androidx.activity", name = "activity-ktx", version.ref = "androidxActivity" } androidx-activity-activityCompose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } androidx-fragment-fragmentKtx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "androidxFragment" } -androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTestCore" } diff --git a/extensions-compose/build.gradle.kts b/extensions-compose/build.gradle.kts index dc40ca934..461e457a2 100644 --- a/extensions-compose/build.gradle.kts +++ b/extensions-compose/build.gradle.kts @@ -53,10 +53,6 @@ kotlin { implementation(deps.jetbrains.kotlinx.kotlinxCoroutinesCore) } - android.main.dependencies { - implementation(deps.androidx.activity.activityKtx) - } - jvm.test.dependencies { implementation(deps.jetbrains.compose.ui.uiTestJunit4) implementation(deps.jetbrains.kotlinx.kotlinxCoroutinesSwing) diff --git a/extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.android.kt b/extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.android.kt index 4b75e1466..1de8adf5a 100644 --- a/extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.android.kt +++ b/extensions-compose/src/androidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/LayoutCorners.android.kt @@ -21,7 +21,6 @@ import androidx.compose.ui.platform.LocalView import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection -import androidx.core.content.ContextCompat.getSystemService internal actual fun Modifier.withLayoutCorners(block: Modifier.(LayoutCorners) -> Modifier): Modifier = composed { @@ -48,7 +47,7 @@ private fun Context.getScreenInfo(density: Density): ScreenInfo? { return null } - val windowMetrics = requireNotNull(getSystemService(this, WindowManager::class.java)).maximumWindowMetrics + val windowMetrics = getSystemService(WindowManager::class.java)?.maximumWindowMetrics ?: return null val insets = windowMetrics.windowInsets return with(density) { From 8e9f6e48dd6587ea620849a1001ccd25734804fc Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 14 Mar 2024 21:12:19 +0000 Subject: [PATCH 81/89] Bumped version to 3.0.0-alpha09 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index a6d08e1cd..4a1e14fdf 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "3.0.0-alpha08" +decompose = "3.0.0-alpha09" kotlin = "1.9.23" essenty = "2.0.0-alpha07" reaktive = "1.2.3" From 16cc4ad0154f5942ac3cb9995d080337031188ef Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 29 Mar 2024 13:20:12 +0000 Subject: [PATCH 82/89] Discard back gesture in PredictiveBackGestureOverlay when swiped in the opposite direction --- .../PredictiveBackGestureOverlay.kt | 66 ++++++++++++------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt index 29d316c6e..4599627f9 100644 --- a/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt +++ b/extensions-compose/src/nonAndroidMain/kotlin/com/arkivanov/decompose/extensions/compose/stack/animation/predictiveback/PredictiveBackGestureOverlay.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.AwaitPointerEventScope import androidx.compose.ui.input.pointer.PointerEventPass +import androidx.compose.ui.input.pointer.PointerId import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.layout @@ -24,6 +25,7 @@ import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import com.arkivanov.decompose.ExperimentalDecomposeApi +import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.BackGestureHandler.Edge import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.backhandler.BackDispatcher import com.arkivanov.essenty.backhandler.BackEvent @@ -125,7 +127,7 @@ private fun Modifier.handleBackGestures( edgeWidth: Dp, activationOffsetThreshold: Dp, confirmationProgressThreshold: Float, - onIconMoved: (position: Offset, progress: Float, BackGestureHandler.Edge) -> Unit, + onIconMoved: (position: Offset, progress: Float, Edge) -> Unit, onIconHidden: () -> Unit, ): Modifier = pointerInput(backDispatcher, leftEdgeEnabled, rightEdgeEnabled) { @@ -135,18 +137,24 @@ private fun Modifier.handleBackGestures( val down = awaitFirstDown(pass = PointerEventPass.Initial) val startPosition = down.position - val isLeftInvalid = !leftEdgeEnabled || (startPosition.x > edgeWidth.toPx()) - val isRightInvalid = !rightEdgeEnabled || (startPosition.x < size.width - edgeWidth.toPx()) + val isLeftEdge = leftEdgeEnabled && (startPosition.x < edgeWidth.toPx()) + val isRightEdge = rightEdgeEnabled && (startPosition.x > size.width - edgeWidth.toPx()) - if (isLeftInvalid && isRightInvalid) { - return@awaitEachGesture - } + val edge = + when { + isLeftEdge && isRightEdge -> if (startPosition.x < size.width / 2F) Edge.LEFT else Edge.RIGHT + isLeftEdge -> Edge.LEFT + isRightEdge -> Edge.RIGHT + else -> return@awaitEachGesture + } val handler = BackGestureHandler( + pointerId = down.id, startPosition = startPosition, size = size, - offsetIgnoreThreshold = 16.dp.toPx(), + edge = edge, + ignoreOffsetThreshold = 16.dp.toPx(), activationOffsetThreshold = activationOffsetThreshold.toPx(), progressConfirmationThreshold = confirmationProgressThreshold, backDispatcher = backDispatcher, @@ -168,41 +176,46 @@ private fun Modifier.backIconOffset(position: Offset): Modifier = } } -private fun BackGestureHandler.Edge.toSwipeEdge(): SwipeEdge = +private fun Edge.toSwipeEdge(): SwipeEdge = when (this) { - BackGestureHandler.Edge.LEFT -> SwipeEdge.LEFT - BackGestureHandler.Edge.RIGHT -> SwipeEdge.RIGHT + Edge.LEFT -> SwipeEdge.LEFT + Edge.RIGHT -> SwipeEdge.RIGHT } private data class IconState( val position: Offset = Offset.Zero, val progress: Float = 0F, - val edge: BackGestureHandler.Edge = BackGestureHandler.Edge.RIGHT, + val edge: Edge = Edge.RIGHT, val isVisible: Boolean = false, ) private class BackGestureHandler( + private val pointerId: PointerId, private val startPosition: Offset, private val size: IntSize, - private val offsetIgnoreThreshold: Float, + private val edge: Edge, + private val ignoreOffsetThreshold: Float, private val activationOffsetThreshold: Float, private val progressConfirmationThreshold: Float, private val backDispatcher: BackDispatcher, private val onIconMoved: (position: Offset, progress: Float, Edge) -> Unit, ) { - private val edge = if (startPosition.x < size.width / 2F) Edge.LEFT else Edge.RIGHT private var changesIterator: Iterator? = null private suspend fun AwaitPointerEventScope.awaitChange(): PointerInputChange { - var iterator = changesIterator + while (true) { + var iterator = changesIterator - if ((iterator == null) || !iterator.hasNext()) { - iterator = awaitPointerEvent(pass = PointerEventPass.Initial).changes.iterator() - changesIterator = iterator - } + while ((iterator == null) || !iterator.hasNext()) { + iterator = awaitPointerEvent(pass = PointerEventPass.Initial).changes.iterator() + changesIterator = iterator + } - return iterator.next() + iterator.next().takeIf { it.id == pointerId }?.also { + return it + } + } } suspend fun AwaitPointerEventScope.handleGesture() { @@ -216,19 +229,24 @@ private class BackGestureHandler( val change = awaitChange() val position = change.position - if ((position.y < startPosition.y - offsetIgnoreThreshold) || (position.y > startPosition.y + offsetIgnoreThreshold)) { + if (!change.pressed || + (position.y < startPosition.y - ignoreOffsetThreshold) || + (position.y > startPosition.y + ignoreOffsetThreshold) + ) { return null } when (edge) { Edge.LEFT -> - if (position.x > startPosition.x + activationOffsetThreshold) { - return change + when { + position.x < startPosition.x - ignoreOffsetThreshold -> return null + position.x > startPosition.x + activationOffsetThreshold -> return change } Edge.RIGHT -> - if (position.x < startPosition.x - activationOffsetThreshold) { - return change + when { + position.x > startPosition.x + ignoreOffsetThreshold -> return null + position.x < startPosition.x - activationOffsetThreshold -> return change } } } From 8d16f302d402538e8a0319be5cb5577a2d551b62 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Wed, 3 Apr 2024 19:29:46 +0100 Subject: [PATCH 83/89] Print configurations on uniqueness violation --- .../arkivanov/decompose/router/children/ChildrenNavigator.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt index 43f0320cb..17b3addba 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt @@ -128,7 +128,9 @@ internal class ChildrenNavigator>( private fun switch(newStates: List>) { val newConfigurations = newStates.mapTo(HashSet(), ChildNavState::configuration) - check(newConfigurations.size == newStates.size) { "Configurations must be unique" } + check(newConfigurations.size == newStates.size) { + "Configurations must be unique: ${newStates.map(ChildNavState::configuration)}." + } val oldItems = items.associateBy(ChildItem::configuration) val newItems = prepareNewItems(newStates = newStates, oldItems = oldItems) From 378623cc03cb999ef2bd12aa777acbe0d8717ab8 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 5 Apr 2024 14:22:03 +0100 Subject: [PATCH 84/89] Updated Essenty to 2.0.0-beta01 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 4a1e14fdf..6d2784cb4 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -2,7 +2,7 @@ decompose = "3.0.0-alpha09" kotlin = "1.9.23" -essenty = "2.0.0-alpha07" +essenty = "2.0.0-beta01" reaktive = "1.2.3" junit = "4.13.2" jetbrainsCompose = "1.6.1" From 556628dbe2f3566e365a59f4d0ccd54762ec8004 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Fri, 5 Apr 2024 14:48:54 +0100 Subject: [PATCH 85/89] Bumped version to 3.0.0-beta01 --- deps.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.versions.toml b/deps.versions.toml index 6d2784cb4..6969acc73 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,6 +1,6 @@ [versions] -decompose = "3.0.0-alpha09" +decompose = "3.0.0-beta01" kotlin = "1.9.23" essenty = "2.0.0-beta01" reaktive = "1.2.3" From cba5ea2a6cdd7ff34bb676f830e9cf349f86a99e Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Thu, 25 Apr 2024 19:22:21 +0100 Subject: [PATCH 86/89] Added withDeepLink extension function --- decompose/api/android/decompose.api | 4 + .../com/arkivanov/decompose/DeeplinkUtils.kt | 88 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 decompose/src/androidMain/kotlin/com/arkivanov/decompose/DeeplinkUtils.kt diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 897d7e491..d5b1c379a 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -49,6 +49,10 @@ public abstract interface class com/arkivanov/decompose/ComponentContextFactoryO public abstract fun getComponentContextFactory ()Lcom/arkivanov/decompose/ComponentContextFactory; } +public final class com/arkivanov/decompose/DeeplinkUtilsKt { + public static final fun withDeepLink (Landroid/app/Activity;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; +} + public final class com/arkivanov/decompose/DefaultComponentContext : com/arkivanov/decompose/ComponentContext { public fun (Lcom/arkivanov/essenty/lifecycle/Lifecycle;)V public fun (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;)V diff --git a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DeeplinkUtils.kt b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DeeplinkUtils.kt new file mode 100644 index 000000000..c1748b71f --- /dev/null +++ b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DeeplinkUtils.kt @@ -0,0 +1,88 @@ +package com.arkivanov.decompose + +import android.app.Activity +import android.app.TaskStackBuilder +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import androidx.core.os.bundleOf +import androidx.savedstate.SavedStateRegistryOwner + +/** + * Extracts a deep link URL from this [Activity] [Intent], calls [block] + * function with the extracted deep link URL (if any) and returns the result. + * + * It is strongly recommended to always use the `standard` (default) + * `launchMode` for the [Activity] when handling deep links. This function takes + * care of restarting the [Activity] task if needed, in which case the returned + * value is `null`. + * + * Example of creating a root component with deep link support. + * + * ```kotlin + * class MainActivity : AppCompatActivity() { + * override fun onCreate(savedInstanceState: Bundle?) { + * super.onCreate(savedInstanceState) + * + * val root = + * withDeepLink { uri -> + * val itemId = uri?.extractItemId() // Parse the deep link + * DefaultRootComponent( + * componentContext = defaultComponentContext(discardSavedState = itemId != null), + * itemId = itemId, + * ) + * } ?: return + * + * // Display the root component as usual + * } + * + * private fun Uri.extractItemId(): String? = + * TODO("Extract item id from the deep link") + * } + * ``` + */ +@ExperimentalDecomposeApi +fun A.withDeepLink( + block: (deepLink: Uri?) -> T, +): T? where A : Activity, A : SavedStateRegistryOwner { + if (restartIfNeeded()) { + return null + } + + val savedState: Bundle? = savedStateRegistry.consumeRestoredStateForKey(key = KEY_SAVED_DEEP_LINK_STATE) + + @Suppress("DEPRECATION") + val lastDeepLink = savedState?.getParcelable(KEY_LAST_DEEP_LINK) as Uri? + + val deepLink = intent.data + + savedStateRegistry.registerSavedStateProvider(key = KEY_SAVED_DEEP_LINK_STATE) { + bundleOf(KEY_LAST_DEEP_LINK to deepLink) + } + + return block(deepLink.takeUnless { it == lastDeepLink }) +} + +// Derived from https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt;l=1487-1504?q=Intent.flags&ss=androidx%2Fplatform%2Fframeworks%2Fsupport:navigation%2F +private fun Activity.restartIfNeeded(): Boolean { + if ((intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK == 0) || (intent.flags and Intent.FLAG_ACTIVITY_CLEAR_TASK != 0)) { + return false + } + + // Someone called us with NEW_TASK, but we don't know what state our whole + // task stack is in, so we need to manually restart the whole stack to + // ensure we're in a predictably good state. + + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + TaskStackBuilder.create(this).addNextIntentWithParentStack(intent).startActivities() + finish() + // Disable second animation in case where the Activity is created twice. + @Suppress("DEPRECATION") + overridePendingTransition(0, 0) + + return true +} + +private const val KEY_SAVED_DEEP_LINK_STATE = "SAVED_DEEP_LINK_STATE" +private const val KEY_LAST_DEEP_LINK = "LAST_DEEP_LINK" + From c19c8456b43a5089ebe3b21fc66c53cbdd2e1a25 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 27 Apr 2024 14:23:09 +0100 Subject: [PATCH 87/89] Updated Esenty to 2.0.0 --- deps.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps.versions.toml b/deps.versions.toml index 6969acc73..0021e925c 100644 --- a/deps.versions.toml +++ b/deps.versions.toml @@ -1,8 +1,8 @@ [versions] -decompose = "3.0.0-beta01" +decompose = "3.0.0" kotlin = "1.9.23" -essenty = "2.0.0-beta01" +essenty = "2.0.0" reaktive = "1.2.3" junit = "4.13.2" jetbrainsCompose = "1.6.1" From b204bc31844a04f79e73f421f78cf326b141a731 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 27 Apr 2024 14:36:06 +0100 Subject: [PATCH 88/89] Promoted retainedComponent, onDecomposeError, pushNew, pushToFront and LifecycleController APIs to stable --- .../kotlin/com/arkivanov/decompose/RetainedComponent.kt | 2 -- .../com/arkivanov/decompose/errorhandler/ErrorHandlers.kt | 3 --- .../com/arkivanov/decompose/router/stack/StackNavigatorExt.kt | 4 ---- .../extensions/compose/lifecycle/LifecycleController.kt | 2 -- .../extensions/compose/lifecycle/LifecycleControllerTest.kt | 2 -- 5 files changed, 13 deletions(-) diff --git a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt index 017029b8d..b0581cd72 100644 --- a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt +++ b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/RetainedComponent.kt @@ -42,7 +42,6 @@ import kotlinx.serialization.builtins.serializer * otherwise it won't. Default value is `true`. * @param factory a function that returns a new instance of the component. */ -@ExperimentalDecomposeApi fun ComponentActivity.retainedComponent( key: String = "RootRetainedComponent", handleBackButton: Boolean = true, @@ -74,7 +73,6 @@ fun ComponentActivity.retainedComponent( * otherwise it won't. Default value is `true`. * @param factory a function that returns a new instance of the component. */ -@ExperimentalDecomposeApi fun Fragment.retainedComponent( key: String = "RootRetainedComponent", handleBackButton: Boolean = true, diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/errorhandler/ErrorHandlers.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/errorhandler/ErrorHandlers.kt index 4e25eb2fc..db01d1583 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/errorhandler/ErrorHandlers.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/errorhandler/ErrorHandlers.kt @@ -1,9 +1,6 @@ package com.arkivanov.decompose.errorhandler -import com.arkivanov.decompose.ExperimentalDecomposeApi - /** * Called when a non-fatal error has occurred in Decompose. */ -@ExperimentalDecomposeApi var onDecomposeError: (Exception) -> Unit = ::printError diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt index 6b7f2f2b9..d2a8026d5 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt @@ -1,7 +1,5 @@ package com.arkivanov.decompose.router.stack -import com.arkivanov.decompose.ExperimentalDecomposeApi - /** * A convenience method for [StackNavigator.navigate]. */ @@ -33,7 +31,6 @@ inline fun StackNavigator.push(configuration: C, crossinline onComp * @param onComplete called when the navigation is finished (either synchronously or asynchronously). * The `isSuccess` argument is `true` if the component was pushed, `false` otherwise. */ -@ExperimentalDecomposeApi inline fun StackNavigator.pushNew( configuration: C, crossinline onComplete: (isSuccess: Boolean) -> Unit = {}, @@ -53,7 +50,6 @@ inline fun StackNavigator.pushNew( * * @param onComplete called when the navigation is finished (either synchronously or asynchronously). */ -@ExperimentalDecomposeApi inline fun StackNavigator.pushToFront( configuration: C, crossinline onComplete: () -> Unit = {}, diff --git a/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt b/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt index 0c0da4803..219d8e97e 100644 --- a/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt +++ b/extensions-compose/src/jvmMain/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleController.kt @@ -6,7 +6,6 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.platform.WindowInfo import androidx.compose.ui.window.WindowState -import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.create @@ -30,7 +29,6 @@ import kotlinx.coroutines.flow.combine * @param windowInfo an optional [WindowInfo] providing the window focused state, see [WindowInfo.isWindowFocused]. * If not provided, the window is always considered focused. */ -@ExperimentalDecomposeApi @Composable fun LifecycleController( lifecycleRegistry: LifecycleRegistry, diff --git a/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleControllerTest.kt b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleControllerTest.kt index 5644b6612..8dec59232 100644 --- a/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleControllerTest.kt +++ b/extensions-compose/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/lifecycle/LifecycleControllerTest.kt @@ -6,14 +6,12 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.window.WindowState -import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.LifecycleRegistry import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals -@OptIn(ExperimentalDecomposeApi::class) @Suppress("TestFunctionName") class LifecycleControllerTest { From 7e4612f9b98551e34e61f0f77f4dac28b3207888 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Sat, 27 Apr 2024 14:14:33 +0100 Subject: [PATCH 89/89] Renamed withDeepLink to handleDeepLink --- decompose/api/android/decompose.api | 2 +- .../com/arkivanov/decompose/DeeplinkUtils.kt | 27 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index d5b1c379a..df83efac9 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -50,7 +50,7 @@ public abstract interface class com/arkivanov/decompose/ComponentContextFactoryO } public final class com/arkivanov/decompose/DeeplinkUtilsKt { - public static final fun withDeepLink (Landroid/app/Activity;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static final fun handleDeepLink (Landroid/app/Activity;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } public final class com/arkivanov/decompose/DefaultComponentContext : com/arkivanov/decompose/ComponentContext { diff --git a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DeeplinkUtils.kt b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DeeplinkUtils.kt index c1748b71f..293bfa5c6 100644 --- a/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DeeplinkUtils.kt +++ b/decompose/src/androidMain/kotlin/com/arkivanov/decompose/DeeplinkUtils.kt @@ -12,10 +12,12 @@ import androidx.savedstate.SavedStateRegistryOwner * Extracts a deep link URL from this [Activity] [Intent], calls [block] * function with the extracted deep link URL (if any) and returns the result. * + * This function must be called from [Activity.onCreate] method. + * * It is strongly recommended to always use the `standard` (default) * `launchMode` for the [Activity] when handling deep links. This function takes - * care of restarting the [Activity] task if needed, in which case the returned - * value is `null`. + * care of restarting the [Activity] task and finishing this [Activity] if needed, + * in which case the returned value is `null`. * * Example of creating a root component with deep link support. * @@ -25,7 +27,7 @@ import androidx.savedstate.SavedStateRegistryOwner * super.onCreate(savedInstanceState) * * val root = - * withDeepLink { uri -> + * handleDeepLink { uri -> * val itemId = uri?.extractItemId() // Parse the deep link * DefaultRootComponent( * componentContext = defaultComponentContext(discardSavedState = itemId != null), @@ -42,28 +44,25 @@ import androidx.savedstate.SavedStateRegistryOwner * ``` */ @ExperimentalDecomposeApi -fun A.withDeepLink( - block: (deepLink: Uri?) -> T, +fun A.handleDeepLink( + block: (Uri?) -> T, ): T? where A : Activity, A : SavedStateRegistryOwner { if (restartIfNeeded()) { return null } val savedState: Bundle? = savedStateRegistry.consumeRestoredStateForKey(key = KEY_SAVED_DEEP_LINK_STATE) - - @Suppress("DEPRECATION") - val lastDeepLink = savedState?.getParcelable(KEY_LAST_DEEP_LINK) as Uri? - - val deepLink = intent.data + val isDeepLinkHandled = savedState?.getBoolean(KEY_DEEP_LINK_HANDLED) ?: false + val deepLink = intent.data.takeUnless { isDeepLinkHandled } savedStateRegistry.registerSavedStateProvider(key = KEY_SAVED_DEEP_LINK_STATE) { - bundleOf(KEY_LAST_DEEP_LINK to deepLink) + bundleOf(KEY_DEEP_LINK_HANDLED to (isDeepLinkHandled || (deepLink != null))) } - return block(deepLink.takeUnless { it == lastDeepLink }) + return block(deepLink) } -// Derived from https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt;l=1487-1504?q=Intent.flags&ss=androidx%2Fplatform%2Fframeworks%2Fsupport:navigation%2F +// Derived from https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt;l=1486;drc=fd7d0dc4a56c2aef65424db7986aa057f9717661 private fun Activity.restartIfNeeded(): Boolean { if ((intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK == 0) || (intent.flags and Intent.FLAG_ACTIVITY_CLEAR_TASK != 0)) { return false @@ -84,5 +83,5 @@ private fun Activity.restartIfNeeded(): Boolean { } private const val KEY_SAVED_DEEP_LINK_STATE = "SAVED_DEEP_LINK_STATE" -private const val KEY_LAST_DEEP_LINK = "LAST_DEEP_LINK" +private const val KEY_DEEP_LINK_HANDLED = "DEEP_LINK_HANDLED"