Skip to content

Fixed resumed child when parent is not resumed #65

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

Merged
merged 5 commits into from
Nov 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Fixed resumed child bug
  • Loading branch information
i.karenkov committed Nov 6, 2024
commit 4b15c7c9a0016e58558d5501d9f6bf0a1f938999
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ class ModoScreenAndroidAdapter private constructor(
private val atomicParentLifecycleOwner = AtomicReference<LifecycleOwner>()
private val application: Application? get() = atomicContext.get()?.applicationContext?.getApplication()

/**
* Indicates that lifecycle should be moved to RESUMED state as soon as parent will be RESUMED.
*/
@Volatile
private var isResumePostponed: Boolean = false

init {
controller.performAttach()
enableSavedStateHandles()
Expand Down Expand Up @@ -135,7 +141,15 @@ class ModoScreenAndroidAdapter private constructor(
}

override fun onResume() {
safeHandleLifecycleEvent(ON_RESUME)
val parentState = atomicParentLifecycleOwner.get()?.lifecycle?.currentState
if (parentState == null || parentState != Lifecycle.State.RESUMED) {
// We need to do so, because child is ready to move to resumed state, when parent is not.
// It can happen when we display this screen as a first screen inside container, that is animating.
// So we need to wait until parent will be resumed and execute ON_RESUME event after it.
isResumePostponed = true
} else {
safeHandleLifecycleEvent(ON_RESUME)
}
}

override fun toString(): String = "${ModoScreenAndroidAdapter::class.simpleName}, screenKey: ${screen.screenKey}"
Expand Down Expand Up @@ -239,10 +253,15 @@ class ModoScreenAndroidAdapter private constructor(
if (
needPropagateLifecycleEventFromParent(
event,
isResumePostponed = isResumePostponed,
isActivityFinishing = activity?.isFinishing,
isChangingConfigurations = activity?.isChangingConfigurations
)
) {
// reset postpone resume flag because we moved state to resumed
if (event == ON_RESUME) {
isResumePostponed = false
}
safeHandleLifecycleEvent(event)
}
}
Expand Down Expand Up @@ -298,6 +317,7 @@ class ModoScreenAndroidAdapter private constructor(
@JvmStatic
fun needPropagateLifecycleEventFromParent(
event: Lifecycle.Event,
isResumePostponed: Boolean,
isActivityFinishing: Boolean?,
isChangingConfigurations: Boolean?
) =
Expand All @@ -312,11 +332,17 @@ class ModoScreenAndroidAdapter private constructor(
*
* In the case of Fragments, we unsubscribe before ON_DESTROY event, so there is no problem with this.
*/
if (event == ON_DESTROY && (isActivityFinishing == false || isChangingConfigurations == true)) {
false
} else {
// Parent can only move lifecycle state down. Because parent cant be already resumed, but child is not, because of running animation.
event !in moveLifecycleStateUpEvents
when {
event == ON_DESTROY && (isActivityFinishing == false || isChangingConfigurations == true) -> {
false
}
isResumePostponed && event == ON_RESUME -> {
true
}
else -> {
// Parent can only move lifecycle state down. Because parent cant be already resumed, but child is not, because of running animation.
event !in moveLifecycleStateUpEvents
}
}

@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import kotlinx.parcelize.Parcelize

typealias StackNavModel = NavModel<StackState, StackAction>

fun StackNavModel(stack: List<Screen>) = StackNavModel(StackState(stack))
fun StackNavModel(screen: Screen) = StackNavModel(listOf(screen))
fun StackNavModel(stack: List<Screen>): StackNavModel = StackNavModel(StackState(stack))
fun StackNavModel(screen: Screen): StackNavModel = StackNavModel(listOf(screen))
fun StackNavModel(vararg screens: Screen): StackNavModel = StackNavModel(screens.toList())

@Stable
interface StackNavContainer : NavigationContainer<StackState, StackAction>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.github.terrakok.modo.sample.screens.stack.StackActionsScreen
import com.github.terrakok.modo.sample.screens.viewmodel.AndroidViewModelSampleScreen
import com.github.terrakok.modo.stack.LocalStackNavigation
import com.github.terrakok.modo.stack.StackNavContainer
import com.github.terrakok.modo.stack.StackNavModel
import com.github.terrakok.modo.stack.back
import com.github.terrakok.modo.stack.forward
import com.github.terrakok.modo.util.getActivity
Expand Down Expand Up @@ -164,6 +165,25 @@ private fun rememberButtons(
buttons = listOf(
ModoButtonSpec("Screen Lifecycle") { navigation?.forward(LifecycleSampleScreen(i + 1)) },
ModoButtonSpec("Keyboard + Lifecycle") { navigation?.forward(KeyboardWithLifecycleScreen()) },
ModoButtonSpec("Open predefined flow") {
navigation?.forward(
CustomStackSample(
i + 1,
StackNavModel(
MainScreen(i + 2),
MainScreen(i + 3),
KeyboardWithLifecycleScreen(),
CustomStackSample(
i = i + 4,
navModel = StackNavModel(
MainScreen(i + 5),
KeyboardWithLifecycleScreen(),
),
),
)
)
)
},
)
),
GroupedButtonsState.Group(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.github.terrakok.modo.ScreenKey
import com.github.terrakok.modo.generateScreenKey
import com.github.terrakok.modo.sample.screens.ModoButton
import com.github.terrakok.modo.sample.screens.ModoButtonSpec
import com.github.terrakok.modo.sample.screens.base.LogLifecycle
import com.github.terrakok.modo.stack.LocalStackNavigation
import com.github.terrakok.modo.stack.back
import com.github.terrakok.modo.util.getActivity
Expand All @@ -39,6 +40,7 @@ class KeyboardWithLifecycleScreen(
) : Screen {
@Composable
override fun Content(modifier: Modifier) {
LogLifecycle()
Column(modifier.windowInsetsPadding(WindowInsets.systemBars)) {
val stackNavigation = LocalStackNavigation.current
ModoButton(ModoButtonSpec("Back") { stackNavigation.back() })
Expand Down