Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update stateflow for lifecycle #256

Merged
merged 4 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ actual fun PreComposeApp(

override fun onResume(owner: androidx.lifecycle.LifecycleOwner) {
super.onResume(owner)
viewModel.lifecycleRegistry.currentState = Lifecycle.State.Active
viewModel.lifecycleRegistry.updateState(Lifecycle.State.Active)
}

override fun onPause(owner: androidx.lifecycle.LifecycleOwner) {
super.onPause(owner)
viewModel.lifecycleRegistry.currentState = Lifecycle.State.InActive
viewModel.lifecycleRegistry.updateState(Lifecycle.State.InActive)
}

// override fun onDestroy(owner: androidx.lifecycle.LifecycleOwner) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ internal class PreComposeViewModel :
}

override fun onCleared() {
lifecycleRegistry.currentState = Lifecycle.State.Destroyed
lifecycleRegistry.updateState(Lifecycle.State.Destroyed)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package moe.tlaster.precompose.lifecycle

import kotlinx.coroutines.flow.StateFlow

interface Lifecycle {
enum class State {
Initialized,
Expand All @@ -9,6 +11,7 @@ interface Lifecycle {
}

val currentState: State
val currentStateFlow: StateFlow<State>
fun removeObserver(observer: LifecycleObserver)
fun addObserver(observer: LifecycleObserver)
fun hasObserver(): Boolean
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package moe.tlaster.precompose.lifecycle

import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.compositionLocalOf

interface LifecycleOwner {
Expand All @@ -8,6 +10,15 @@ interface LifecycleOwner {

val LocalLifecycleOwner = compositionLocalOf<LifecycleOwner> { noLocalProvidedFor("LocalLifecycleOwner") }

/**
* Returns current composition local value for the owner.
* @throws IllegalStateException if no value was provided.
*/
val currentLocalLifecycleOwner: LifecycleOwner
@Composable
@ReadOnlyComposable
get() = LocalLifecycleOwner.current

private fun noLocalProvidedFor(name: String): Nothing {
error("CompositionLocal $name not present")
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,46 @@
package moe.tlaster.precompose.lifecycle

import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class LifecycleRegistry : Lifecycle {
private val observers = arrayListOf<LifecycleObserver>()
override var currentState: Lifecycle.State = Lifecycle.State.Initialized
private var observers: List<LifecycleObserver> = emptyList()
private val _currentStateFlow = MutableStateFlow(Lifecycle.State.Initialized)
override val currentStateFlow: StateFlow<Lifecycle.State> = _currentStateFlow.asStateFlow()

private var _state: Lifecycle.State = _currentStateFlow.value
set(value) {
if (field == Lifecycle.State.Destroyed || value == Lifecycle.State.Initialized) {
observers = emptyList()
return
}
field = value
_currentStateFlow.value = value
dispatchState(value)
}

override val currentState: Lifecycle.State get() = _state

fun updateState(value: Lifecycle.State) {
_state = value
}

private fun dispatchState(value: Lifecycle.State) {
observers.toMutableList().forEach {
observers.forEach {
it.onStateChanged(value)
}
}

override fun removeObserver(observer: LifecycleObserver) {
observers.remove(observer)
observers -= observer
}

override fun addObserver(observer: LifecycleObserver) {
if (observers.contains(observer)) {
return
}
observers.add(observer)
observers += observer
observer.onStateChanged(currentState)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import moe.tlaster.precompose.lifecycle.LocalLifecycleOwner
import moe.tlaster.precompose.lifecycle.currentLocalLifecycleOwner
import moe.tlaster.precompose.ui.DefaultBackHandler
import moe.tlaster.precompose.ui.LocalBackDispatcherOwner

Expand All @@ -26,7 +26,7 @@ fun BackHandler(enabled: Boolean = true, onBack: () -> Unit) {
}
backCallback.isEnabled = enabled
}
val lifecycleOwner = LocalLifecycleOwner.current
val lifecycleOwner = currentLocalLifecycleOwner
DisposableEffect(lifecycleOwner, backDispatcher) {
// Add callback to the backDispatcher
backDispatcher.register(backCallback)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ class BackStackEntry internal constructor(
get() = lifecycleRegistry

fun active() {
lifecycleRegistry.currentState = Lifecycle.State.Active
lifecycleRegistry.updateState(Lifecycle.State.Active)
}

fun inActive() {
lifecycleRegistry.currentState = Lifecycle.State.InActive
lifecycleRegistry.updateState(Lifecycle.State.InActive)
if (_destroyAfterTransition) {
destroy()
}
Expand All @@ -61,7 +61,7 @@ class BackStackEntry internal constructor(
}

internal fun destroyDirectly() {
lifecycleRegistry.currentState = Lifecycle.State.Destroyed
lifecycleRegistry.updateState(Lifecycle.State.Destroyed)
stateHolder.close()
parentStateHolder.remove(stateId)
savedStateHolder.close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,14 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import moe.tlaster.precompose.lifecycle.LocalLifecycleOwner
import moe.tlaster.precompose.lifecycle.currentLocalLifecycleOwner
import moe.tlaster.precompose.navigation.route.ComposeRoute
import moe.tlaster.precompose.navigation.route.GroupRoute
import moe.tlaster.precompose.navigation.transition.NavTransition
import moe.tlaster.precompose.stateholder.LocalSavedStateHolder
import moe.tlaster.precompose.stateholder.LocalStateHolder
import moe.tlaster.precompose.stateholder.currentLocalSavedStateHolder
import moe.tlaster.precompose.stateholder.currentLocalStateHolder
import kotlin.math.absoluteValue
import kotlin.math.roundToInt

Expand Down Expand Up @@ -79,9 +82,9 @@ fun NavHost(
swipeProperties: SwipeProperties? = null,
builder: RouteBuilder.() -> Unit,
) {
val lifecycleOwner = requireNotNull(LocalLifecycleOwner.current)
val stateHolder = requireNotNull(LocalStateHolder.current)
val savedStateHolder = requireNotNull(LocalSavedStateHolder.current)
val lifecycleOwner = currentLocalLifecycleOwner
val stateHolder = currentLocalStateHolder
val savedStateHolder = currentLocalSavedStateHolder
val composeStateHolder = rememberSaveableStateHolder()

// true for assuming that lifecycleOwner, stateHolder and composeStateHolder are not changing during the lifetime of the NavHost
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package moe.tlaster.precompose.navigation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.snapshotFlow
import moe.tlaster.precompose.lifecycle.LifecycleOwner
import moe.tlaster.precompose.stateholder.LocalStateHolder
import moe.tlaster.precompose.stateholder.SavedStateHolder
import moe.tlaster.precompose.stateholder.StateHolder
import moe.tlaster.precompose.stateholder.currentLocalStateHolder

/**
* Creates or returns an existing [Navigator] that controls the [NavHost].
Expand All @@ -14,7 +14,7 @@ import moe.tlaster.precompose.stateholder.StateHolder
*/
@Composable
fun rememberNavigator(name: String = ""): Navigator {
val stateHolder = LocalStateHolder.current
val stateHolder = currentLocalStateHolder
return stateHolder.getOrPut("${name}Navigator") {
Navigator()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package moe.tlaster.precompose.stateholder

import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.saveable.SaveableStateRegistry

Expand Down Expand Up @@ -37,3 +39,13 @@ val LocalSavedStateHolder = compositionLocalOf {
// A default implementation for platforms that don't offer a [SaveableStateRegistry]
SavedStateHolder("root", null)
}

/**
* Returns the current [SavedStateHolder] from composition or throws an [IllegalStateException]
* if there is no [SavedStateHolder] in provided.
*/

val currentLocalSavedStateHolder: SavedStateHolder
@Composable
@ReadOnlyComposable
get() = LocalSavedStateHolder.current
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package moe.tlaster.precompose.stateholder

import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.compositionLocalOf

@OptIn(ExperimentalStdlibApi::class)
Expand Down Expand Up @@ -44,3 +46,12 @@ class StateHolder : AutoCloseable {
val LocalStateHolder = compositionLocalOf<StateHolder> {
error("No StateHolder provided")
}

/**
* Returns the current [StateHolder] from composition or throws an [IllegalStateException]
* if there is no [StateHolder] in provided.
*/
val currentLocalStateHolder: StateHolder
@Composable
@ReadOnlyComposable
get() = LocalStateHolder.current
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ class LifecycleTest {
fun lifeCycleTest() {
val lifecycleOwner = TestLifecycleOwner()
assertEquals(Lifecycle.State.Initialized, lifecycleOwner.lifecycle.currentState)
lifecycleOwner.lifecycle.currentState = Lifecycle.State.Active
lifecycleOwner.lifecycle.updateState(Lifecycle.State.Active)
assertEquals(Lifecycle.State.Active, lifecycleOwner.lifecycle.currentState)
lifecycleOwner.lifecycle.currentState = Lifecycle.State.Initialized
lifecycleOwner.lifecycle.updateState(Lifecycle.State.Initialized)
assertEquals(Lifecycle.State.Active, lifecycleOwner.lifecycle.currentState)
lifecycleOwner.lifecycle.currentState = Lifecycle.State.InActive
lifecycleOwner.lifecycle.updateState(Lifecycle.State.InActive)
assertEquals(Lifecycle.State.InActive, lifecycleOwner.lifecycle.currentState)
lifecycleOwner.lifecycle.currentState = Lifecycle.State.Active
lifecycleOwner.lifecycle.updateState(Lifecycle.State.Active)
assertEquals(Lifecycle.State.Active, lifecycleOwner.lifecycle.currentState)
lifecycleOwner.lifecycle.currentState = Lifecycle.State.Destroyed
lifecycleOwner.lifecycle.updateState(Lifecycle.State.Destroyed)
assertEquals(Lifecycle.State.Destroyed, lifecycleOwner.lifecycle.currentState)
lifecycleOwner.lifecycle.currentState = Lifecycle.State.Active
lifecycleOwner.lifecycle.updateState(Lifecycle.State.Active)
assertEquals(Lifecycle.State.Destroyed, lifecycleOwner.lifecycle.currentState)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,11 @@ class BackStackManagerTest {
),
)
val entry = manager.backStacks.value[0]
lifecycleOwner.lifecycle.currentState = Lifecycle.State.Active
lifecycleOwner.lifecycle.updateState(Lifecycle.State.Active)
assertEquals(Lifecycle.State.Active, entry.lifecycle.currentState)
lifecycleOwner.lifecycle.currentState = Lifecycle.State.InActive
lifecycleOwner.lifecycle.updateState(Lifecycle.State.InActive)
assertEquals(Lifecycle.State.InActive, entry.lifecycle.currentState)
lifecycleOwner.lifecycle.currentState = Lifecycle.State.Destroyed
lifecycleOwner.lifecycle.updateState(Lifecycle.State.Destroyed)
assertEquals(0, manager.backStacks.value.size)
assertEquals(Lifecycle.State.Destroyed, entry.lifecycle.currentState)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,25 @@ private class AppStateHolder(
name = UIApplicationWillTerminateNotification,
`object` = null,
)
lifecycle.currentState = Lifecycle.State.Active
lifecycle.updateState(Lifecycle.State.Active)
}

@OptIn(BetaInteropApi::class)
@ObjCAction
fun appMovedToForeground(notification: NSNotification) {
lifecycle.currentState = Lifecycle.State.Active
lifecycle.updateState(Lifecycle.State.Active)
}

@OptIn(BetaInteropApi::class)
@ObjCAction
fun appMovedToBackground(notification: NSNotification) {
lifecycle.currentState = Lifecycle.State.InActive
lifecycle.updateState(Lifecycle.State.InActive)
}

@OptIn(BetaInteropApi::class)
@ObjCAction
fun appWillTerminate(notification: NSNotification) {
lifecycle.currentState = Lifecycle.State.Destroyed
lifecycle.updateState(Lifecycle.State.Destroyed)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,18 @@ actual fun PreComposeApp(
val listener = remember {
object : WindowAdapter() {
override fun windowOpened(e: WindowEvent?) {
holder.lifecycle.currentState = Lifecycle.State.Active
holder.lifecycle.updateState(Lifecycle.State.Active)
}
override fun windowClosed(e: WindowEvent?) {
holder.lifecycle.currentState = Lifecycle.State.Destroyed
holder.lifecycle.updateState(Lifecycle.State.Destroyed)
}
override fun windowStateChanged(e: WindowEvent?) {
when (e?.newState) {
java.awt.Frame.ICONIFIED -> {
holder.lifecycle.currentState = Lifecycle.State.InActive
holder.lifecycle.updateState(Lifecycle.State.InActive)
}
else -> {
holder.lifecycle.currentState = Lifecycle.State.Active
holder.lifecycle.updateState(Lifecycle.State.Active)
}
}
}
Expand Down