diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7fc20ae7..c7eeb653 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,17 +1,17 @@ [versions] # also check project root build.gradle.kts for versions -androidx-animation = "1.5.0" -androidx-foundation = "1.5.0" +androidx-animation = "1.5.3" +androidx-foundation = "1.5.3" androidx-appcompat = "1.6.1" -androidx-coreKtx = "1.10.1" -androidxActivityVer = "1.7.2" +androidx-coreKtx = "1.12.0" +androidxActivityVer = "1.8.0" androidGradlePlugin = "8.1.1" junit = "4.13.2" junitJupiterEngine = "5.10.0" junitJupiterApi = "5.10.0" kotlin = "1.9.10" kotlinxCoroutinesCore = "1.7.3" -lifecycleRuntimeKtx = "2.6.1" -material = "1.5.0" +lifecycleRuntimeKtx = "2.6.2" +material = "1.5.3" moleculeRuntime = "1.2.1" savedstateKtx = "1.2.1" spotless = "6.22.0" @@ -22,9 +22,11 @@ koin-compose = "1.1.0" [libraries] androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "androidxActivityVer" } +androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivityVer" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } androidx-coreKtx = { module = "androidx.core:core-ktx", version.ref = "androidx-coreKtx" } androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" } androidx-material = { module = "androidx.compose.material:material", version.ref = "material" } androidx-savedstate-ktx = { module = "androidx.savedstate:savedstate-ktx", version.ref = "savedstateKtx" } animation = { module = "androidx.compose.animation:animation", version.ref = "androidx-animation" } diff --git a/precompose/build.gradle.kts b/precompose/build.gradle.kts index 8e2a4ca1..f4f5c9fb 100644 --- a/precompose/build.gradle.kts +++ b/precompose/build.gradle.kts @@ -12,7 +12,19 @@ group = "moe.tlaster" version = rootProject.extra.get("precomposeVersion") as String kotlin { - targetHierarchy.default() + targetHierarchy.default { + common { + group("nonAndroid") { + withMacosArm64() + withMacosX64() + withIosX64() + withIosArm64() + withIosSimulatorArm64() + withJvm() + withJs() + } + } + } macosArm64() macosX64() iosX64() @@ -61,6 +73,8 @@ kotlin { api(libs.androidx.appcompat) implementation(libs.androidx.lifecycle.runtime.ktx) api(libs.androidx.savedstate.ktx) + implementation(libs.androidx.lifecycle.viewmodel.compose) + implementation(libs.androidx.activity.compose) } } val androidUnitTest by getting { diff --git a/precompose/src/androidMain/kotlin/moe/tlaster/precompose/PreComposeApp.android.kt b/precompose/src/androidMain/kotlin/moe/tlaster/precompose/PreComposeApp.android.kt new file mode 100644 index 00000000..b0e94dd5 --- /dev/null +++ b/precompose/src/androidMain/kotlin/moe/tlaster/precompose/PreComposeApp.android.kt @@ -0,0 +1,84 @@ +package moe.tlaster.precompose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.LocalSaveableStateRegistry +import androidx.compose.ui.Modifier +import androidx.lifecycle.DefaultLifecycleObserver +import moe.tlaster.precompose.lifecycle.Lifecycle +import moe.tlaster.precompose.lifecycle.LocalLifecycleOwner +import moe.tlaster.precompose.lifecycle.PreComposeViewModel +import moe.tlaster.precompose.stateholder.LocalSavedStateHolder +import moe.tlaster.precompose.stateholder.LocalStateHolder +import moe.tlaster.precompose.stateholder.SavedStateHolder +import moe.tlaster.precompose.ui.LocalBackDispatcherOwner + +@Composable +actual fun PreComposeApp( + modifier: Modifier, + content: @Composable () -> Unit, +) { + val viewModel = androidx.lifecycle.viewmodel.compose.viewModel() + + val lifecycle = androidx.compose.ui.platform.LocalLifecycleOwner.current.lifecycle + val onBackPressedDispatcher = checkNotNull(androidx.activity.compose.LocalOnBackPressedDispatcherOwner.current) { + "No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner" + }.onBackPressedDispatcher + + DisposableEffect(lifecycle) { + val observer = object : DefaultLifecycleObserver { + override fun onCreate(owner: androidx.lifecycle.LifecycleOwner) { + super.onCreate(owner) + onBackPressedDispatcher.addCallback(owner, viewModel.backPressedCallback) + } + + override fun onResume(owner: androidx.lifecycle.LifecycleOwner) { + super.onResume(owner) + viewModel.lifecycleRegistry.currentState = Lifecycle.State.Active + } + + override fun onPause(owner: androidx.lifecycle.LifecycleOwner) { + super.onPause(owner) + viewModel.lifecycleRegistry.currentState = Lifecycle.State.InActive + } + + // override fun onDestroy(owner: androidx.lifecycle.LifecycleOwner) { + // super.onDestroy(owner) + // if (!isChangingConfigurations) { + // viewModel.lifecycleRegistry.currentState = Lifecycle.State.Destroyed + // } + // } + } + lifecycle.addObserver(observer) + onDispose { + lifecycle.removeObserver(observer) + } + } + + val state by viewModel.backDispatcher.canHandleBackPress.collectAsState(false) + + val saveableStateRegistry = LocalSaveableStateRegistry.current + val savedStateHolder = remember(saveableStateRegistry) { + SavedStateHolder( + "root", + saveableStateRegistry, + ) + } + + LaunchedEffect(state) { + viewModel.backPressedCallback.isEnabled = state + } + CompositionLocalProvider( + LocalLifecycleOwner provides viewModel, + LocalStateHolder provides viewModel.stateHolder, + LocalBackDispatcherOwner provides viewModel, + LocalSavedStateHolder provides savedStateHolder, + ) { + content.invoke() + } +} diff --git a/precompose/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt b/precompose/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt index 5aac16cd..82387c09 100644 --- a/precompose/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt +++ b/precompose/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt @@ -2,29 +2,16 @@ package moe.tlaster.precompose.lifecycle import android.view.ViewGroup import androidx.activity.ComponentActivity -import androidx.activity.viewModels import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionContext -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.LocalSaveableStateRegistry import androidx.compose.ui.platform.ComposeView -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.lifecycle.setViewTreeViewModelStoreOwner import androidx.savedstate.findViewTreeSavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner -import moe.tlaster.precompose.stateholder.LocalSavedStateHolder -import moe.tlaster.precompose.stateholder.LocalStateHolder -import moe.tlaster.precompose.stateholder.SavedStateHolder -import moe.tlaster.precompose.ui.LocalBackDispatcherOwner +import moe.tlaster.precompose.PreComposeApp fun ComponentActivity.setContent( parent: CompositionContext? = null, @@ -71,66 +58,7 @@ private fun ComponentActivity.setOwners() { @Composable private fun ComponentActivity.ContentInternal(content: @Composable () -> Unit) { - ProvideAndroidCompositionLocals { - content.invoke() - } -} - -@Composable -private fun ComponentActivity.ProvideAndroidCompositionLocals( - content: @Composable () -> Unit, -) { - val viewModel by viewModels() - - DisposableEffect(lifecycle) { - val observer = object : DefaultLifecycleObserver { - override fun onCreate(owner: LifecycleOwner) { - super.onCreate(owner) - onBackPressedDispatcher.addCallback(owner, viewModel.backPressedCallback) - } - - override fun onResume(owner: LifecycleOwner) { - super.onResume(owner) - viewModel.lifecycleRegistry.currentState = Lifecycle.State.Active - } - - override fun onPause(owner: LifecycleOwner) { - super.onPause(owner) - viewModel.lifecycleRegistry.currentState = Lifecycle.State.InActive - } - - override fun onDestroy(owner: LifecycleOwner) { - super.onDestroy(owner) - if (!isChangingConfigurations) { - viewModel.lifecycleRegistry.currentState = Lifecycle.State.Destroyed - } - } - } - lifecycle.addObserver(observer) - onDispose { - lifecycle.removeObserver(observer) - } - } - - val state by viewModel.backDispatcher.canHandleBackPress.collectAsState(false) - - val saveableStateRegistry = LocalSaveableStateRegistry.current - val savedStateHolder = remember(saveableStateRegistry) { - SavedStateHolder( - "root", - saveableStateRegistry, - ) - } - - LaunchedEffect(state) { - viewModel.backPressedCallback.isEnabled = state - } - CompositionLocalProvider( - LocalLifecycleOwner provides viewModel, - LocalStateHolder provides viewModel.stateHolder, - LocalBackDispatcherOwner provides viewModel, - LocalSavedStateHolder provides savedStateHolder, - ) { + PreComposeApp { content.invoke() } } diff --git a/precompose/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeViewModel.kt b/precompose/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeViewModel.kt index 3ec26daa..a09025b4 100644 --- a/precompose/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeViewModel.kt +++ b/precompose/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeViewModel.kt @@ -27,4 +27,8 @@ internal class PreComposeViewModel : backDispatcher.onBackPress() } } + + override fun onCleared() { + lifecycleRegistry.currentState = Lifecycle.State.Destroyed + } } diff --git a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/PreComposeApp.kt b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/PreComposeApp.kt new file mode 100644 index 00000000..ede26bf1 --- /dev/null +++ b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/PreComposeApp.kt @@ -0,0 +1,10 @@ +package moe.tlaster.precompose + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +expect fun PreComposeApp( + modifier: Modifier = Modifier, + content: @Composable () -> Unit = {}, +) diff --git a/precompose/src/nonAndroidMain/kotlin/moe/tlaster/precompose/PreComposeApp.nonAndroid.kt b/precompose/src/nonAndroidMain/kotlin/moe/tlaster/precompose/PreComposeApp.nonAndroid.kt new file mode 100644 index 00000000..03ebb8e7 --- /dev/null +++ b/precompose/src/nonAndroidMain/kotlin/moe/tlaster/precompose/PreComposeApp.nonAndroid.kt @@ -0,0 +1,43 @@ +package moe.tlaster.precompose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import moe.tlaster.precompose.lifecycle.LifecycleOwner +import moe.tlaster.precompose.lifecycle.LifecycleRegistry +import moe.tlaster.precompose.lifecycle.LocalLifecycleOwner +import moe.tlaster.precompose.stateholder.LocalStateHolder +import moe.tlaster.precompose.stateholder.StateHolder +import moe.tlaster.precompose.ui.BackDispatcher +import moe.tlaster.precompose.ui.BackDispatcherOwner +import moe.tlaster.precompose.ui.LocalBackDispatcherOwner + +@Composable +actual fun PreComposeApp( + modifier: Modifier, + content: @Composable () -> Unit, +) { + val holder = remember { + PreComposeWindowHolder() + } + CompositionLocalProvider( + LocalLifecycleOwner provides holder, + LocalStateHolder provides holder.stateHolder, + LocalBackDispatcherOwner provides holder, + ) { + content.invoke() + } +} + +class PreComposeWindowHolder : LifecycleOwner, BackDispatcherOwner { + override val lifecycle by lazy { + LifecycleRegistry() + } + val stateHolder by lazy { + StateHolder() + } + override val backDispatcher by lazy { + BackDispatcher() + } +}