diff --git a/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/common/SaveableLaunchedEffect.kt b/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/common/SaveableLaunchedEffect.kt new file mode 100644 index 00000000..04d5dd04 --- /dev/null +++ b/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/common/SaveableLaunchedEffect.kt @@ -0,0 +1,20 @@ +package fr.paug.androidmakers.wear.ui.common + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable + +@Composable +fun SaveableLaunchedEffect( + key: Any, + block: suspend () -> Unit +) { + val lastLaunchedKey = rememberSaveable { mutableStateOf(null) } + if (lastLaunchedKey.value != key) { + lastLaunchedKey.value = key + LaunchedEffect(Unit) { + block() + } + } +} diff --git a/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/main/MainActivity.kt b/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/main/MainActivity.kt index 3c16569a..3d52f966 100644 --- a/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/main/MainActivity.kt +++ b/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/main/MainActivity.kt @@ -17,6 +17,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.core.app.ActivityCompat import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.navigation.NavType import androidx.navigation.navArgument import androidx.wear.compose.foundation.SwipeToDismissBoxState @@ -39,6 +41,7 @@ import fr.paug.androidmakers.wear.ui.session.list.SessionListScreen import fr.paug.androidmakers.wear.ui.settings.SettingsScreen import fr.paug.androidmakers.wear.ui.signin.SignInScreen import fr.paug.androidmakers.wear.ui.theme.AndroidMakersWearTheme +import kotlinx.coroutines.flow.map import org.koin.androidx.compose.koinViewModel import org.koin.core.parameter.parametersOf @@ -86,6 +89,8 @@ fun WearApp( AndroidMakersWearTheme { AppScaffold { + val isResumed by LocalLifecycleOwner.current.lifecycle.currentStateFlow.map { it == Lifecycle.State.RESUMED } + .collectAsState(false) SwipeDismissableNavHost( navController = navController, startDestination = Navigation.main, @@ -94,6 +99,7 @@ fun WearApp( composable(Navigation.main) { MainScreen( viewModel = viewModel, + isResumed = isResumed, swipeToDismissBoxState = swipeToDismissBoxState, onSignInClick = onSignInClick, onSignOutClick = { viewModel.signOut() }, @@ -125,6 +131,7 @@ fun WearApp( @Composable fun MainScreen( viewModel: MainViewModel, + isResumed: Boolean, swipeToDismissBoxState: SwipeToDismissBoxState, onSignInClick: () -> Unit, onSignOutClick: () -> Unit, @@ -140,7 +147,8 @@ fun MainScreen( modifier = Modifier .fillMaxSize() .edgeSwipeToDismiss(swipeToDismissBoxState), - state = pagerState + state = pagerState, + beyondViewportPageCount = 2, ) { page -> when (page) { 0 -> { @@ -156,6 +164,7 @@ fun MainScreen( SessionListScreen( sessions = sessionsDay1, title = stringResource(id = R.string.main_day1), + isResumed = isResumed, onSessionClick = onSessionClick ) } @@ -164,6 +173,7 @@ fun MainScreen( SessionListScreen( sessions = sessionsDay2, title = stringResource(id = R.string.main_day2), + isResumed = isResumed, onSessionClick = onSessionClick ) } diff --git a/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/session/list/SessionListScreen.kt b/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/session/list/SessionListScreen.kt index 32e9908d..0b5a270a 100644 --- a/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/session/list/SessionListScreen.kt +++ b/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/session/list/SessionListScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Bookmark import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -45,6 +44,7 @@ import com.google.android.horologist.compose.material.ResponsiveListHeader import fr.paug.androidmakers.wear.R import fr.paug.androidmakers.wear.ui.common.Loading import fr.paug.androidmakers.wear.ui.common.PulsatingRedDot +import fr.paug.androidmakers.wear.ui.common.SaveableLaunchedEffect import fr.paug.androidmakers.wear.ui.session.UISession import fr.paug.androidmakers.wear.ui.session.uiSessions import fr.paug.androidmakers.wear.ui.theme.amRed @@ -53,6 +53,7 @@ import fr.paug.androidmakers.wear.ui.theme.amRed fun SessionListScreen( sessions: List?, title: String, + isResumed: Boolean, onSessionClick: (String) -> Unit, ) { if (sessions == null) { @@ -61,6 +62,7 @@ fun SessionListScreen( SessionList( sessions = sessions, title = title, + isResumed = isResumed, onSessionClick = onSessionClick, ) } @@ -70,6 +72,7 @@ fun SessionListScreen( private fun SessionList( sessions: List, title: String, + isResumed: Boolean, onSessionClick: (String) -> Unit, ) { val columnState = rememberResponsiveColumnState( @@ -79,13 +82,13 @@ private fun SessionList( ) ) - // Approximation of about half the height of a card, so the card is centered when scrolling to it. - // Maybe there's a way to get the actual height? - val scrollOffset = with(LocalDensity.current) { 80.dp.roundToPx() } - - val nextSessionIndex = sessions.nextSessionIndex() - if (nextSessionIndex > 0) { - LaunchedEffect(Unit) { + val density = LocalDensity.current + SaveableLaunchedEffect(isResumed) { + val nextSessionIndex = sessions.nextSessionIndex() + if (nextSessionIndex > 0) { + // Approximation of about half the height of a card, so the card is centered when scrolling to it. + // Maybe there's a way to get the actual height? + val scrollOffset = with(density) { 80.dp.roundToPx() } columnState.state.scrollToItem( // Add 1 to the index to account for the title index = nextSessionIndex + 1, @@ -220,12 +223,12 @@ private fun SessionItem( @WearPreviewFontScales @Composable private fun LoadingSessionListScreenPreview() { - SessionListScreen(null, stringResource(id = R.string.main_day1), {}) + SessionListScreen(null, stringResource(id = R.string.main_day1), true, {}) } @WearPreviewDevices @WearPreviewFontScales @Composable private fun LoadedSessionListScreenPreview() { - SessionListScreen(uiSessions, stringResource(id = R.string.main_day1), {}) + SessionListScreen(uiSessions, stringResource(id = R.string.main_day1), true, {}) }