diff --git a/JetNews/app/build.gradle.kts b/JetNews/app/build.gradle.kts index 6fee8c50b0..335ae5dfb6 100644 --- a/JetNews/app/build.gradle.kts +++ b/JetNews/app/build.gradle.kts @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { alias(libs.plugins.android.application) alias(libs.plugins.compose) + alias(libs.plugins.kotlin.serialization) } android { @@ -92,6 +93,7 @@ dependencies { implementation(libs.kotlin.stdlib) implementation(libs.kotlinx.coroutines.android) + implementation(libs.kotlinx.serialization.core) implementation(libs.androidx.compose.animation) implementation(libs.androidx.compose.foundation.layout) @@ -116,7 +118,9 @@ dependencies { implementation(libs.androidx.lifecycle.livedata.ktx) implementation(libs.androidx.lifecycle.viewModelCompose) implementation(libs.androidx.lifecycle.runtime.compose) + implementation(libs.androidx.lifecycle.viewmodel.navigation3) implementation(libs.androidx.navigation.compose) + implementation(libs.androidx.navigation3.ui) implementation(libs.androidx.window) androidTestImplementation(libs.junit) diff --git a/JetNews/app/src/main/AndroidManifest.xml b/JetNews/app/src/main/AndroidManifest.xml index 134af83d20..ddbddb1676 100644 --- a/JetNews/app/src/main/AndroidManifest.xml +++ b/JetNews/app/src/main/AndroidManifest.xml @@ -40,6 +40,14 @@ android:pathPrefix="/jetnews" android:scheme="https" /> + + + + + + Unit, navigateToInterests: () -> Unit, closeDrawer: () -> Unit, @@ -58,7 +61,7 @@ fun AppDrawer( NavigationDrawerItem( label = { Text(stringResource(id = R.string.home_title)) }, icon = { Icon(painterResource(R.drawable.ic_home), null) }, - selected = currentRoute == JetnewsDestinations.HOME_ROUTE, + selected = currentKey is HomeKey, onClick = { navigateToHome() closeDrawer() @@ -68,7 +71,7 @@ fun AppDrawer( NavigationDrawerItem( label = { Text(stringResource(id = R.string.interests_title)) }, icon = { Icon(painterResource(R.drawable.ic_list_alt), null) }, - selected = currentRoute == JetnewsDestinations.INTERESTS_ROUTE, + selected = currentKey == InterestsKey, onClick = { navigateToInterests() closeDrawer() @@ -102,7 +105,7 @@ fun PreviewAppDrawer() { JetnewsTheme { AppDrawer( drawerState = rememberDrawerState(initialValue = DrawerValue.Open), - currentRoute = JetnewsDestinations.HOME_ROUTE, + currentKey = HomeKey(), navigateToHome = {}, navigateToInterests = {}, closeDrawer = { }, diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsApp.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsApp.kt index e0acc812e1..59d57e52e1 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsApp.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsApp.kt @@ -23,40 +23,37 @@ import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.rememberDrawerState import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController +import androidx.navigation3.runtime.NavKey import com.example.jetnews.data.AppContainer import com.example.jetnews.ui.components.AppNavRail +import com.example.jetnews.ui.navigation.HomeKey +import com.example.jetnews.ui.navigation.JetnewsNavDisplay +import com.example.jetnews.ui.navigation.NavigationState +import com.example.jetnews.ui.navigation.Navigator +import com.example.jetnews.ui.navigation.rememberNavigationState import com.example.jetnews.ui.theme.JetnewsTheme import kotlinx.coroutines.launch @Composable -fun JetnewsApp(appContainer: AppContainer, widthSizeClass: WindowWidthSizeClass) { +fun JetnewsApp(appContainer: AppContainer, widthSizeClass: WindowWidthSizeClass, deeplinkKey: HomeKey?) { JetnewsTheme { - val navController = rememberNavController() - val navigationActions = remember(navController) { - JetnewsNavigationActions(navController) - } - val coroutineScope = rememberCoroutineScope() - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentRoute = - navBackStackEntry?.destination?.route ?: JetnewsDestinations.HOME_ROUTE - val isExpandedScreen = widthSizeClass == WindowWidthSizeClass.Expanded val sizeAwareDrawerState = rememberSizeAwareDrawerState(isExpandedScreen) + val navigationState = rememberNavigationState(deeplinkKey ?: HomeKey()) + val navigator = Navigator(navigationState) + ModalNavigationDrawer( drawerContent = { AppDrawer( drawerState = sizeAwareDrawerState, - currentRoute = currentRoute, - navigateToHome = navigationActions.navigateToHome, - navigateToInterests = navigationActions.navigateToInterests, + currentKey = navigationState.currentKey, + navigateToHome = navigator::toHome, + navigateToInterests = navigator::toInterests, closeDrawer = { coroutineScope.launch { sizeAwareDrawerState.close() } }, ) }, @@ -67,15 +64,16 @@ fun JetnewsApp(appContainer: AppContainer, widthSizeClass: WindowWidthSizeClass) Row { if (isExpandedScreen) { AppNavRail( - currentRoute = currentRoute, - navigateToHome = navigationActions.navigateToHome, - navigateToInterests = navigationActions.navigateToInterests, + currentKey = navigationState.currentKey, + navigateToHome = navigator::toHome, + navigateToInterests = navigator::toInterests, ) } - JetnewsNavGraph( + JetnewsNavDisplay( + navigationState = navigationState, appContainer = appContainer, + onBack = navigator::pop, isExpandedScreen = isExpandedScreen, - navController = navController, openDrawer = { coroutineScope.launch { sizeAwareDrawerState.open() } }, ) } diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsNavGraph.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsNavGraph.kt deleted file mode 100644 index 8e8202e592..0000000000 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsNavGraph.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.jetnews.ui - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController -import androidx.navigation.navDeepLink -import com.example.jetnews.JetnewsApplication.Companion.JETNEWS_APP_URI -import com.example.jetnews.data.AppContainer -import com.example.jetnews.ui.home.HomeRoute -import com.example.jetnews.ui.home.HomeViewModel -import com.example.jetnews.ui.interests.InterestsRoute -import com.example.jetnews.ui.interests.InterestsViewModel - -const val POST_ID = "postId" - -@Composable -fun JetnewsNavGraph( - appContainer: AppContainer, - isExpandedScreen: Boolean, - modifier: Modifier = Modifier, - navController: NavHostController = rememberNavController(), - openDrawer: () -> Unit = {}, - startDestination: String = JetnewsDestinations.HOME_ROUTE, -) { - NavHost( - navController = navController, - startDestination = startDestination, - modifier = modifier, - ) { - composable( - route = JetnewsDestinations.HOME_ROUTE, - deepLinks = listOf( - navDeepLink { - uriPattern = - "$JETNEWS_APP_URI/${JetnewsDestinations.HOME_ROUTE}?$POST_ID={$POST_ID}" - }, - ), - ) { navBackStackEntry -> - val homeViewModel: HomeViewModel = viewModel( - factory = HomeViewModel.provideFactory( - postsRepository = appContainer.postsRepository, - preSelectedPostId = navBackStackEntry.arguments?.getString(POST_ID), - ), - ) - HomeRoute( - homeViewModel = homeViewModel, - isExpandedScreen = isExpandedScreen, - openDrawer = openDrawer, - ) - } - composable(JetnewsDestinations.INTERESTS_ROUTE) { - val interestsViewModel: InterestsViewModel = viewModel( - factory = InterestsViewModel.provideFactory(appContainer.interestsRepository), - ) - InterestsRoute( - interestsViewModel = interestsViewModel, - isExpandedScreen = isExpandedScreen, - openDrawer = openDrawer, - ) - } - } -} diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsNavigation.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsNavigation.kt deleted file mode 100644 index 8dd1ee01d6..0000000000 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/JetnewsNavigation.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.jetnews.ui - -import androidx.navigation.NavGraph.Companion.findStartDestination -import androidx.navigation.NavHostController - -/** - * Destinations used in the [JetnewsApp]. - */ -object JetnewsDestinations { - const val HOME_ROUTE = "home" - const val INTERESTS_ROUTE = "interests" -} - -/** - * Models the navigation actions in the app. - */ -class JetnewsNavigationActions(navController: NavHostController) { - val navigateToHome: () -> Unit = { - navController.navigate(JetnewsDestinations.HOME_ROUTE) { - // Pop up to the start destination of the graph to - // avoid building up a large stack of destinations - // on the back stack as users select items - popUpTo(navController.graph.findStartDestination().id) { - saveState = true - } - // Avoid multiple copies of the same destination when - // reselecting the same item - launchSingleTop = true - // Restore state when reselecting a previously selected item - restoreState = true - } - } - val navigateToInterests: () -> Unit = { - navController.navigate(JetnewsDestinations.INTERESTS_ROUTE) { - popUpTo(navController.graph.findStartDestination().id) { - saveState = true - } - launchSingleTop = true - restoreState = true - } - } -} diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/MainActivity.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/MainActivity.kt index bd6d2ac0bd..635364cd66 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/MainActivity.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/MainActivity.kt @@ -16,13 +16,18 @@ package com.example.jetnews.ui +import android.content.Intent +import android.net.Uri import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass +import androidx.navigation3.runtime.NavKey import com.example.jetnews.JetnewsApplication +import com.example.jetnews.ui.navigation.HomeKey +import com.example.jetnews.ui.navigation.POST_ID class MainActivity : ComponentActivity() { @@ -34,7 +39,18 @@ class MainActivity : ComponentActivity() { val appContainer = (application as JetnewsApplication).container setContent { val widthSizeClass = calculateWindowSizeClass(this).widthSizeClass - JetnewsApp(appContainer, widthSizeClass) + JetnewsApp(appContainer, widthSizeClass, getDeepLinkKey(intent)) } } } + +private fun getDeepLinkKey(intent: Intent): HomeKey? { + val uri: Uri = intent.data ?: return null + val pathParams = uri.pathSegments + if (pathParams.lastOrNull() != "home") return null + + val queryParams = uri.getQueryParameters(POST_ID) + if (queryParams.isEmpty() || queryParams.size > 1) return null + // "https://developer.android.com/jetnews/home?postId={$POST_ID}" + return HomeKey(postId = Uri.decode(queryParams.firstOrNull())) +} diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/components/AppNavRail.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/components/AppNavRail.kt index b6d48e74f2..e400039d69 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/components/AppNavRail.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/components/AppNavRail.kt @@ -30,12 +30,14 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.navigation3.runtime.NavKey import com.example.jetnews.R -import com.example.jetnews.ui.JetnewsDestinations +import com.example.jetnews.ui.navigation.HomeKey +import com.example.jetnews.ui.navigation.InterestsKey import com.example.jetnews.ui.theme.JetnewsTheme @Composable -fun AppNavRail(currentRoute: String, navigateToHome: () -> Unit, navigateToInterests: () -> Unit, modifier: Modifier = Modifier) { +fun AppNavRail(currentKey: NavKey, navigateToHome: () -> Unit, navigateToInterests: () -> Unit, modifier: Modifier = Modifier) { NavigationRail( header = { Icon( @@ -49,14 +51,14 @@ fun AppNavRail(currentRoute: String, navigateToHome: () -> Unit, navigateToInter ) { Spacer(Modifier.weight(1f)) NavigationRailItem( - selected = currentRoute == JetnewsDestinations.HOME_ROUTE, + selected = currentKey is HomeKey, onClick = navigateToHome, icon = { Icon(painterResource(id = R.drawable.ic_home), stringResource(R.string.home_title)) }, label = { Text(stringResource(R.string.home_title)) }, alwaysShowLabel = false, ) NavigationRailItem( - selected = currentRoute == JetnewsDestinations.INTERESTS_ROUTE, + selected = currentKey == InterestsKey, onClick = navigateToInterests, icon = { Icon(painterResource(id = R.drawable.ic_list_alt), stringResource(R.string.interests_title)) }, label = { Text(stringResource(R.string.interests_title)) }, @@ -72,7 +74,7 @@ fun AppNavRail(currentRoute: String, navigateToHome: () -> Unit, navigateToInter fun PreviewAppNavRail() { JetnewsTheme { AppNavRail( - currentRoute = JetnewsDestinations.HOME_ROUTE, + currentKey = HomeKey(), navigateToHome = {}, navigateToInterests = {}, ) diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/navigation/JetnewsNavDisplay.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/navigation/JetnewsNavDisplay.kt new file mode 100644 index 0000000000..61b1cf1d05 --- /dev/null +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/navigation/JetnewsNavDisplay.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.jetnews.ui.navigation + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation3.runtime.EntryProviderScope +import androidx.navigation3.runtime.NavKey +import androidx.navigation3.runtime.entryProvider +import androidx.navigation3.ui.NavDisplay +import com.example.jetnews.data.AppContainer +import com.example.jetnews.data.interests.InterestsRepository +import com.example.jetnews.data.posts.PostsRepository +import com.example.jetnews.ui.home.HomeRoute +import com.example.jetnews.ui.home.HomeViewModel +import com.example.jetnews.ui.interests.InterestsRoute +import com.example.jetnews.ui.interests.InterestsViewModel + +const val POST_ID = "postId" + +@Composable +fun JetnewsNavDisplay( + navigationState: NavigationState, + appContainer: AppContainer, + onBack: () -> Unit, + isExpandedScreen: Boolean, + modifier: Modifier = Modifier, + openDrawer: () -> Unit = {}, +) { + + val decoratedEntries = navigationState.getEntries( + entryProvider = entryProvider { + homeEntry( + appContainer.postsRepository, + isExpandedScreen, + openDrawer, + ) + interestsEntry( + appContainer.interestsRepository, + isExpandedScreen, + openDrawer, + ) + }, + ) + + NavDisplay( + entries = decoratedEntries, + modifier = modifier, + onBack = onBack, + ) +} + +private fun EntryProviderScope.homeEntry(postsRepository: PostsRepository, isExpandedScreen: Boolean, openDrawer: () -> Unit) { + entry { + val homeViewModel: HomeViewModel = viewModel( + factory = HomeViewModel.provideFactory( + postsRepository = postsRepository, + preSelectedPostId = it.postId, + ), + ) + HomeRoute( + homeViewModel = homeViewModel, + isExpandedScreen = isExpandedScreen, + openDrawer = openDrawer, + ) + } +} + +private fun EntryProviderScope.interestsEntry( + interestsRepository: InterestsRepository, + isExpandedScreen: Boolean, + openDrawer: () -> Unit, +) { + entry { + val interestsViewModel: InterestsViewModel = viewModel( + factory = InterestsViewModel.provideFactory(interestsRepository), + ) + InterestsRoute( + interestsViewModel = interestsViewModel, + isExpandedScreen = isExpandedScreen, + openDrawer = openDrawer, + ) + } +} diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/navigation/JetnewsNavigation.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/navigation/JetnewsNavigation.kt new file mode 100644 index 0000000000..8d3f8f1f22 --- /dev/null +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/navigation/JetnewsNavigation.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.jetnews.ui.navigation + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.runtime.toMutableStateList +import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator +import androidx.navigation3.runtime.NavBackStack +import androidx.navigation3.runtime.NavEntry +import androidx.navigation3.runtime.NavKey +import androidx.navigation3.runtime.rememberDecoratedNavEntries +import androidx.navigation3.runtime.rememberNavBackStack +import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator +import kotlinx.serialization.Serializable + +@Serializable +data class HomeKey(val postId: String? = null) : NavKey + +@Serializable +object InterestsKey : NavKey + +fun getNavigationKeys(startKey: HomeKey) = listOf(startKey, InterestsKey) + +@Composable +fun rememberNavigationState(startKey: HomeKey): NavigationState { + val currentKeys = rememberNavBackStack(startKey) + val backStacks = buildMap { + getNavigationKeys(startKey) + .forEach { + put(it::class.toString(), rememberNavBackStack(it)) + } + }.toMutableMap() + return remember(startKey) { NavigationState(currentKeys, backStacks) } +} + +/** + * Contains the navigation state + */ +class NavigationState(val currentKeys: NavBackStack, val backStacks: MutableMap>) { + + val currentKey + get() = currentKeys.last() + val currentBackStack + get() = backStacks[currentKey::class.toString()] + + @Composable + fun getEntries(entryProvider: (NavKey) -> NavEntry): SnapshotStateList> { + val decoratedEntries = backStacks.mapValues { (_, stack) -> + val decorators = listOf( + rememberSaveableStateHolderNavEntryDecorator(), + rememberViewModelStoreNavEntryDecorator(), + ) + rememberDecoratedNavEntries( + backStack = stack, + entryDecorators = decorators, + entryProvider = entryProvider, + ) + } + + return currentKeys + .flatMap { decoratedEntries[it::class.toString()]!! } + .toMutableStateList() + } +} + +/** + * Models the navigation actions in the app. + */ +class Navigator(val state: NavigationState) { + fun toHome(postId: String? = null) { + val newKey = HomeKey(postId) + if (state.currentKey == newKey) return + + state.currentKeys.clear() + state.currentKeys.add(newKey) + state.backStacks[newKey::class.toString()]?.apply { + clear() + add(newKey) + } + } + + fun toInterests() { + if (state.currentKey == InterestsKey) return + state.currentKeys.add(InterestsKey) + } + + fun pop() { + state.currentKeys.removeLastOrNull() + } +} diff --git a/JetNews/gradle/libs.versions.toml b/JetNews/gradle/libs.versions.toml index b9be095d92..c1b8ca2a6e 100644 --- a/JetNews/gradle/libs.versions.toml +++ b/JetNews/gradle/libs.versions.toml @@ -17,6 +17,7 @@ androidx-lifecycle = "2.8.2" androidx-lifecycle-compose = "2.10.0" androidx-lifecycle-runtime-compose = "2.10.0" androidx-navigation = "2.9.7" +androidx-navigation3 = "1.1.0-alpha03" androidx-palette = "1.0.0" androidx-test = "1.7.0" androidx-test-espresso = "3.7.0" @@ -40,7 +41,7 @@ horologist = "0.7.15" jdkDesugar = "2.1.5" junit = "4.13.2" kotlin = "2.3.0" -kotlinx-serialization-json = "1.10.0" +kotlinx-serialization = "1.10.0" kotlinx_immutable = "0.4.0" ksp = "2.3.5" maps-compose = "8.0.0" @@ -102,10 +103,12 @@ androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-ru androidx-lifecycle-viewModelCompose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle-compose" } androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle-compose" } androidx-lifecycle-viewmodel-savedstate = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "androidx-lifecycle-compose" } +androidx-lifecycle-viewmodel-navigation3 = { module = "androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "androidx-lifecycle-compose" } androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" } androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "androidx-navigation" } androidx-navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "androidx-navigation" } +androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "androidx-navigation3" } androidx-palette = { module = "androidx.palette:palette", version.ref = "androidx-palette" } androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } @@ -148,7 +151,8 @@ kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.re kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlinx_immutable" } kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } -kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" } +kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" } +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" } okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" } okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } play-services-wearable = { module = "com.google.android.gms:play-services-wearable", version.ref = "play-services-wearable" }