diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 60661a90f..b0e45122f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -84,24 +84,24 @@ dependencies { implementation(projects.core.designsystem) implementation(projects.core.data) implementation(projects.core.model) - implementation(projects.core.navigation) implementation(projects.core.analytics) implementation(projects.core.navigation) implementation(projects.sync.work) implementation(libs.androidx.activity.compose) implementation(libs.androidx.compose.material3) - implementation(libs.androidx.navigation3.runtime) implementation(libs.androidx.navigation3.ui) implementation(libs.androidx.compose.material3.adaptive) implementation(libs.androidx.compose.material3.adaptive.layout) implementation(libs.androidx.compose.material3.adaptive.navigation) + implementation(libs.androidx.compose.material3.adaptive.navigation3) implementation(libs.androidx.compose.material3.windowSizeClass) implementation(libs.androidx.compose.runtime.tracing) implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.splashscreen) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.lifecycle.runtimeCompose) + implementation(libs.androidx.lifecycle.viewModel.navigation3) implementation(libs.androidx.navigation.compose) implementation(libs.androidx.profileinstaller) implementation(libs.androidx.tracing.ktx) diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt index 03557441b..64539c4dd 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt @@ -43,6 +43,7 @@ import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey import com.google.samples.apps.nowinandroid.ui.NiaApp import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState import com.google.samples.apps.nowinandroid.util.isSystemInDarkTheme @@ -80,7 +81,7 @@ class MainActivity : ComponentActivity() { lateinit var niaBackStack: NiaBackStack @Inject - lateinit var entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder.() -> Unit> + lateinit var entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder.() -> Unit> override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/di/BackStackProvider.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/di/BackStackProvider.kt index aa089a4d8..174bd677f 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/di/BackStackProvider.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/di/BackStackProvider.kt @@ -26,7 +26,7 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) -object NiaAppNavigation { +object BackStackProvider { @Provides @Singleton fun provideNiaBackStack(): NiaBackStack = diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavDisplay.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavDisplay.kt index 48ab8a7b3..45f0e61b2 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavDisplay.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavDisplay.kt @@ -16,23 +16,36 @@ package com.google.samples.apps.nowinandroid.navigation +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.navigation3.rememberListDetailSceneStrategy import androidx.compose.runtime.Composable import androidx.compose.runtime.compositionLocalOf -import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator import androidx.navigation3.runtime.EntryProviderBuilder -import androidx.navigation3.runtime.NavEntryDecorator import androidx.navigation3.runtime.entryProvider +import androidx.navigation3.runtime.rememberSavedStateNavEntryDecorator import androidx.navigation3.ui.NavDisplay +import androidx.navigation3.ui.rememberSceneSetupNavEntryDecorator import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey +@OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun NiaNavDisplay( niaBackStack: NiaBackStack, - entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder.() -> Unit>, + entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder.() -> Unit>, ) { + val listDetailStrategy = rememberListDetailSceneStrategy() + NavDisplay( backStack = niaBackStack.backStack, + sceneStrategy = listDetailStrategy, onBack = { niaBackStack.removeLast() }, + entryDecorators = listOf( + rememberSceneSetupNavEntryDecorator(), + rememberSavedStateNavEntryDecorator(), + rememberViewModelStoreNavEntryDecorator(), + ), entryProvider = entryProvider { entryProviderBuilders.forEach { builder -> builder() diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt index ec4224efd..eb3e78b22 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt @@ -20,7 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.compose.NavHost import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.bookmarksScreen -import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouBaseRoute +import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouRoute import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.forYouSection import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.navigateToInterests import com.google.samples.apps.nowinandroid.feature.search.api.navigation.searchScreen @@ -46,7 +46,7 @@ fun NiaNavHost( val navController = appState.navController NavHost( navController = navController, - startDestination = ForYouBaseRoute, + startDestination = ForYouRoute, modifier = modifier, ) { forYouSection( diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt index edce9517a..0ee6008e7 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt @@ -20,8 +20,8 @@ import androidx.annotation.StringRes import androidx.compose.ui.graphics.vector.ImageVector import com.google.samples.apps.nowinandroid.R import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksRoute -import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouBaseRoute import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouRoute import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute import kotlin.reflect.KClass @@ -49,8 +49,7 @@ enum class TopLevelDestination( @StringRes val iconTextId: Int, @StringRes val titleTextId: Int, val route: KClass<*>, - val baseRoute: KClass<*> = route, - val key: Any + val key: NiaBackStackKey ) { FOR_YOU( selectedIcon = NiaIcons.Upcoming, @@ -58,8 +57,7 @@ enum class TopLevelDestination( iconTextId = forYouR.string.feature_foryou_api_title, titleTextId = R.string.app_name, route = ForYouRoute::class, - baseRoute = ForYouBaseRoute::class, - key = ForYouBaseRoute + key = ForYouRoute ), BOOKMARKS( selectedIcon = NiaIcons.Bookmarks, @@ -78,3 +76,5 @@ enum class TopLevelDestination( key = InterestsRoute(null) ), } + +internal val TopLevelDestinations = TopLevelDestination.entries.associateBy { dest -> dest.key } diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt index 9a65765c0..d0e246489 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt @@ -71,7 +71,9 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopAp import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColors import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.navigation.LocalSnackbarHostState +import com.google.samples.apps.nowinandroid.feature.search.api.navigation.navigateToSearch import com.google.samples.apps.nowinandroid.feature.settings.api.SettingsDialog import com.google.samples.apps.nowinandroid.navigation.NiaNavDisplay import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination @@ -81,7 +83,7 @@ import com.google.samples.apps.nowinandroid.feature.settings.api.R as settingsR @Composable fun NiaApp( appState: NiaAppState, - entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder.() -> Unit>, + entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder.() -> Unit>, modifier: Modifier = Modifier, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(), ) { @@ -132,7 +134,7 @@ fun NiaApp( ) internal fun NiaApp( appState: NiaAppState, - entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder.() -> Unit>, + entryProviderBuilders: Set<@JvmSuppressWildcards EntryProviderBuilder.() -> Unit>, showSettingsDialog: Boolean, onSettingsDismissed: () -> Unit, onTopAppBarActionClick: () -> Unit, @@ -141,7 +143,7 @@ internal fun NiaApp( ) { val unreadDestinations by appState.topLevelDestinationsWithUnreadResources .collectAsStateWithLifecycle() - val currentTopLevelKey = appState.currentTopLevelDestination + val currentTopLevelKey = appState.currentTopLevelDestination!!.key if (showSettingsDialog) { @@ -156,10 +158,7 @@ internal fun NiaApp( navigationSuiteItems = { appState.topLevelDestinations.forEach { destination -> val hasUnread = unreadDestinations.contains(destination) -// val selected = currentDestination -// .isRouteInHierarchy(destination.baseRoute) val selected = destination.key == currentTopLevelKey - println("cfok destination:$destination, currentDest:$currentTopLevelKey") item( selected = selected, onClick = { appState.navigateToTopLevelDestination(destination) }, @@ -233,7 +232,7 @@ internal fun NiaApp( containerColor = Color.Transparent, ), onActionClick = { onTopAppBarActionClick() }, - onNavigationClick = { appState.navigateToSearchNav3() }, + onNavigationClick = { appState.niaBackStack.navigateToSearch() }, ) } diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt index 0eba2d0db..91a0bddfb 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt @@ -35,6 +35,7 @@ import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.BOOKMARKS import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.FOR_YOU import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.INTERESTS +import com.google.samples.apps.nowinandroid.navigation.TopLevelDestinations import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -98,12 +99,7 @@ class NiaAppState( // } val currentTopLevelDestination: TopLevelDestination? - @Composable get() { - return TopLevelDestination.entries.firstOrNull { topLevelDestination -> - topLevelDestination.key == niaBackStack.currentTopLevelKey -// currentDestination?.hasRoute(route = topLevelDestination.route) == true - } - } + @Composable get() = TopLevelDestinations[niaBackStack.currentTopLevelKey] val isOffline = networkMonitor.isOnline .map(Boolean::not) @@ -178,9 +174,6 @@ class NiaAppState( } fun navigateToSearch() = navController.navigateToSearch() - fun navigateToSearchNav3() = niaBackStack.navigateToSearch( - onInterestsClick = { navigateToTopLevelDestination(INTERESTS) } - ) } /** diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts index 3ee05ccb8..e2c42a274 100644 --- a/core/navigation/build.gradle.kts +++ b/core/navigation/build.gradle.kts @@ -4,5 +4,5 @@ plugins { } dependencies { - implementation(libs.androidx.navigation3.runtime) + api(libs.androidx.navigation3.runtime) } diff --git a/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NiaBackStack.kt b/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NiaBackStack.kt index 73934fe77..0b0962206 100644 --- a/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NiaBackStack.kt +++ b/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NiaBackStack.kt @@ -16,7 +16,6 @@ package com.google.samples.apps.nowinandroid.core.navigation -import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf @@ -26,12 +25,12 @@ import javax.inject.Inject import kotlin.collections.remove class NiaBackStack @Inject constructor( - startKey: Any, + startKey: NiaBackStackKey, ) { val backStack = mutableStateListOf(startKey) // Maintain a stack for each top level route - private var topLevelStacks : LinkedHashMap> = linkedMapOf( + private var topLevelStacks : LinkedHashMap> = linkedMapOf( startKey to mutableStateListOf(startKey) ) @@ -39,8 +38,8 @@ class NiaBackStack @Inject constructor( var currentTopLevelKey by mutableStateOf(startKey) private set - internal val currentKey: Any - @Composable get() = topLevelStacks[currentTopLevelKey]!!.last() + internal val currentKey: NiaBackStackKey + get() = topLevelStacks[currentTopLevelKey]!!.last() private fun updateBackStack() = backStack.apply { @@ -48,7 +47,7 @@ class NiaBackStack @Inject constructor( addAll(topLevelStacks.flatMap { it.value }) } - fun navigateToTopLevelDestination(key: Any){ + fun navigateToTopLevelDestination(key: NiaBackStackKey){ // If the top level doesn't exist, add it if (topLevelStacks[key] == null){ topLevelStacks.put(key, mutableStateListOf(key)) @@ -60,14 +59,16 @@ class NiaBackStack @Inject constructor( } } } + currentTopLevelKey = key updateBackStack() } - fun navigate(key: Any){ - println("cfok navigate $key") - topLevelStacks[currentTopLevelKey]?.add(key) - updateBackStack() + fun navigate(key: NiaBackStackKey){ + if (backStack.lastOrNull() != key) { + topLevelStacks[currentTopLevelKey]?.add(key) + updateBackStack() + } } fun removeLast(){ @@ -77,5 +78,6 @@ class NiaBackStack @Inject constructor( currentTopLevelKey = topLevelStacks.keys.last() updateBackStack() } +} -} \ No newline at end of file +interface NiaBackStackKey \ No newline at end of file diff --git a/feature/bookmarks/api/build.gradle.kts b/feature/bookmarks/api/build.gradle.kts index 9ab1fbd05..2771838ef 100644 --- a/feature/bookmarks/api/build.gradle.kts +++ b/feature/bookmarks/api/build.gradle.kts @@ -21,3 +21,7 @@ plugins { android { namespace = "com.google.samples.apps.nowinandroid.feature.bookmarks.api" } + +dependencies { + api(projects.core.navigation) +} diff --git a/feature/bookmarks/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/api/navigation/BookmarksNavigation.kt b/feature/bookmarks/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/api/navigation/BookmarksNavigation.kt index 132424350..eeac9c13f 100644 --- a/feature/bookmarks/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/api/navigation/BookmarksNavigation.kt +++ b/feature/bookmarks/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/api/navigation/BookmarksNavigation.kt @@ -19,9 +19,10 @@ package com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey import kotlinx.serialization.Serializable -@Serializable object BookmarksRoute +@Serializable object BookmarksRoute: NiaBackStackKey fun NavController.navigateToBookmarks(navOptions: NavOptions) = navigate(route = BookmarksRoute, navOptions) diff --git a/feature/bookmarks/impl/build.gradle.kts b/feature/bookmarks/impl/build.gradle.kts index 2f62df27d..738daac67 100644 --- a/feature/bookmarks/impl/build.gradle.kts +++ b/feature/bookmarks/impl/build.gradle.kts @@ -26,7 +26,6 @@ android { dependencies { implementation(projects.core.data) implementation(projects.feature.bookmarks.api) - implementation(projects.core.navigation) implementation(projects.feature.topic.api) testImplementation(projects.core.testing) diff --git a/feature/bookmarks/impl/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/BookmarksScreenTest.kt b/feature/bookmarks/impl/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/BookmarksScreenTest.kt index 56577976d..84a527cd5 100644 --- a/feature/bookmarks/impl/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/BookmarksScreenTest.kt +++ b/feature/bookmarks/impl/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/BookmarksScreenTest.kt @@ -35,6 +35,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.testing.TestLifecycleOwner import com.google.samples.apps.nowinandroid.core.testing.data.userNewsResourcesTestData +import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -63,7 +64,7 @@ class BookmarksScreenTest { composeTestRule .onNodeWithContentDescription( - composeTestRule.activity.resources.getString(R.string.feature_bookmarks_api_loading), + composeTestRule.activity.resources.getString(R.string.feature_bookmarks_impl_loading), ) .assertExists() } @@ -160,13 +161,13 @@ class BookmarksScreenTest { composeTestRule .onNodeWithText( - composeTestRule.activity.getString(R.string.feature_bookmarks_api_empty_error), + composeTestRule.activity.getString(R.string.feature_bookmarks_impl_empty_error), ) .assertExists() composeTestRule .onNodeWithText( - composeTestRule.activity.getString(R.string.feature_bookmarks_api_empty_description), + composeTestRule.activity.getString(R.string.feature_bookmarks_impl_empty_description), ) .assertExists() } diff --git a/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/BookmarksScreen.kt b/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/BookmarksScreen.kt index e7383be3a..23fc65552 100644 --- a/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/BookmarksScreen.kt +++ b/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/BookmarksScreen.kt @@ -76,7 +76,7 @@ import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParam import com.google.samples.apps.nowinandroid.core.ui.newsFeed @Composable -internal fun BookmarksRoute( +internal fun BookmarksScreen( onTopicClick: (String) -> Unit, onShowSnackbar: suspend (String, String?) -> Boolean, modifier: Modifier = Modifier, diff --git a/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/navigation/BookmarksEntryProvider.kt b/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/navigation/BookmarksEntryProvider.kt index 0bfeb3668..2df50cbb5 100644 --- a/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/navigation/BookmarksEntryProvider.kt +++ b/feature/bookmarks/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/impl/navigation/BookmarksEntryProvider.kt @@ -23,8 +23,9 @@ import androidx.compose.runtime.compositionLocalOf import androidx.navigation3.runtime.EntryProviderBuilder import androidx.navigation3.runtime.entry import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksRoute -import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.BookmarksRoute +import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.BookmarksScreen import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic import dagger.Module import dagger.Provides @@ -40,10 +41,10 @@ object BookmarksModule { @IntoSet fun provideBookmarksEntryProviderBuilder( backStack: NiaBackStack, - ): EntryProviderBuilder.() -> @JvmSuppressWildcards Unit = { + ): EntryProviderBuilder.() -> Unit = { entry { val snackbarHostState = LocalSnackbarHostState.current - BookmarksRoute( + BookmarksScreen( onTopicClick = backStack::navigateToTopic, onShowSnackbar = { message, action -> snackbarHostState.showSnackbar( @@ -58,5 +59,5 @@ object BookmarksModule { } val LocalSnackbarHostState = compositionLocalOf { - error("host state should be initialzied at runtime") + error("SnackbarHostState state should be initialized at runtime") } diff --git a/feature/foryou/api/build.gradle.kts b/feature/foryou/api/build.gradle.kts index 45eb87d89..2ca069d64 100644 --- a/feature/foryou/api/build.gradle.kts +++ b/feature/foryou/api/build.gradle.kts @@ -24,16 +24,5 @@ android { } dependencies { - implementation(libs.accompanist.permissions) - implementation(projects.core.data) - implementation(projects.core.domain) - implementation(projects.core.notifications) - - testImplementation(libs.hilt.android.testing) - testImplementation(libs.robolectric) - testImplementation(projects.core.testing) - testDemoImplementation(projects.core.screenshotTesting) - - androidTestImplementation(libs.bundles.androidx.compose.ui.test) - androidTestImplementation(projects.core.testing) + api(projects.core.navigation) } diff --git a/feature/foryou/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/api/navigation/ForYouNavigation.kt b/feature/foryou/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/api/navigation/ForYouNavigation.kt index e11b4d26e..fa8c9f6d5 100644 --- a/feature/foryou/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/api/navigation/ForYouNavigation.kt +++ b/feature/foryou/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/api/navigation/ForYouNavigation.kt @@ -19,11 +19,10 @@ package com.google.samples.apps.nowinandroid.feature.foryou.api.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey import kotlinx.serialization.Serializable -@Serializable data object ForYouRoute // route to ForYou screen - -@Serializable data object ForYouBaseRoute // route to base navigation graph +@Serializable data object ForYouRoute: NiaBackStackKey // route to ForYou screen fun NavController.navigateToForYou(navOptions: NavOptions) = navigate(route = ForYouRoute, navOptions) diff --git a/feature/foryou/impl/build.gradle.kts b/feature/foryou/impl/build.gradle.kts index 00a3c1419..4abc2c7ac 100644 --- a/feature/foryou/impl/build.gradle.kts +++ b/feature/foryou/impl/build.gradle.kts @@ -29,7 +29,6 @@ dependencies { implementation(projects.core.data) implementation(projects.core.domain) implementation(projects.core.notifications) - implementation(projects.core.navigation) implementation(projects.feature.foryou.api) implementation(projects.feature.topic.api) diff --git a/feature/foryou/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/navigation/ForYouEntryProvider.kt b/feature/foryou/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/navigation/ForYouEntryProvider.kt index 05863011c..de3c6bf53 100644 --- a/feature/foryou/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/navigation/ForYouEntryProvider.kt +++ b/feature/foryou/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/navigation/ForYouEntryProvider.kt @@ -19,7 +19,8 @@ package com.google.samples.apps.nowinandroid.feature.foryou.impl.navigation import androidx.navigation3.runtime.EntryProviderBuilder import androidx.navigation3.runtime.entry import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack -import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouBaseRoute +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey +import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouRoute import com.google.samples.apps.nowinandroid.feature.foryou.impl.ForYouScreen import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic import dagger.Module @@ -36,8 +37,8 @@ object ForYouModule { @IntoSet fun provideForYouEntryProviderBuilder( backStack: NiaBackStack, - ): EntryProviderBuilder.() -> @JvmSuppressWildcards Unit = { - entry { + ): EntryProviderBuilder.() -> Unit = { + entry { ForYouScreen( onTopicClick = backStack::navigateToTopic ) diff --git a/feature/interests/api/build.gradle.kts b/feature/interests/api/build.gradle.kts index db804484e..28a2f964f 100644 --- a/feature/interests/api/build.gradle.kts +++ b/feature/interests/api/build.gradle.kts @@ -23,5 +23,5 @@ android { } dependencies { - implementation(projects.core.navigation) + api(projects.core.navigation) } \ No newline at end of file diff --git a/feature/interests/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/api/navigation/InterestsNavigation.kt b/feature/interests/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/api/navigation/InterestsNavigation.kt index f4ae878f7..e2c7e9059 100644 --- a/feature/interests/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/api/navigation/InterestsNavigation.kt +++ b/feature/interests/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/api/navigation/InterestsNavigation.kt @@ -19,12 +19,13 @@ package com.google.samples.apps.nowinandroid.feature.interests.api.navigation import androidx.navigation.NavController import androidx.navigation.NavOptions import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey import kotlinx.serialization.Serializable @Serializable data class InterestsRoute( // The ID of the topic which will be initially selected at this destination val initialTopicId: String? = null, -) +): NiaBackStackKey fun NavController.navigateToInterests( initialTopicId: String? = null, @@ -37,4 +38,4 @@ fun NiaBackStack.navigateToInterests( initialTopicId: String? = null, ) { navigate(InterestsRoute(initialTopicId)) -} +} \ No newline at end of file diff --git a/feature/interests/impl/build.gradle.kts b/feature/interests/impl/build.gradle.kts index 6419f4b1e..a5e6e3a06 100644 --- a/feature/interests/impl/build.gradle.kts +++ b/feature/interests/impl/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation(libs.androidx.compose.material3.adaptive) implementation(libs.androidx.compose.material3.adaptive.layout) implementation(libs.androidx.compose.material3.adaptive.navigation) + implementation(libs.androidx.compose.material3.adaptive.navigation3) testImplementation(projects.core.testing) testImplementation(libs.robolectric) diff --git a/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsEntryProvider.kt b/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsEntryProvider.kt index e485aed89..f4973a3e0 100644 --- a/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsEntryProvider.kt +++ b/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsEntryProvider.kt @@ -16,11 +16,15 @@ package com.google.samples.apps.nowinandroid.feature.interests.impl -import androidx.compose.runtime.snapshots.SnapshotStateList -import androidx.navigation.compose.composable +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.navigation3.ListDetailSceneStrategy import androidx.navigation3.runtime.EntryProviderBuilder import androidx.navigation3.runtime.entry +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute +import com.google.samples.apps.nowinandroid.feature.topic.api.TopicDetailPlaceholder +import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -31,11 +35,22 @@ import dagger.multibindings.IntoSet @InstallIn(ActivityComponent::class) object InterestsModule { + @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Provides @IntoSet - fun provideInterestsEntryProviderBuilder(): EntryProviderBuilder.() -> @JvmSuppressWildcards Unit = { - entry { key -> - InterestsListDetailScreen() + fun provideInterestsEntryProviderBuilder( + backStack: NiaBackStack + ): EntryProviderBuilder.() -> Unit = { + entry( + metadata = ListDetailSceneStrategy.listPane { + TopicDetailPlaceholder() + } + ) { key -> +// InterestsListDetailScreen() + InterestsScreen( + onTopicClick = backStack::navigateToTopic, + shouldHighlightSelectedTopic = false, + ) } } } \ No newline at end of file diff --git a/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsListDetailScreen.kt b/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsListDetailScreen.kt index 357afd83e..77b1c1357 100644 --- a/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsListDetailScreen.kt +++ b/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsListDetailScreen.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 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. @@ -165,7 +165,7 @@ internal fun InterestsListDetailScreen( } }, ) { - InterestsRoute( + InterestsScreen( onTopicClick = ::onTopicClickShowDetailPane, shouldHighlightSelectedTopic = listDetailNavigator.isDetailPaneVisible(), ) diff --git a/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsScreen.kt b/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsScreen.kt index 24e698303..225f8a7a9 100644 --- a/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsScreen.kt +++ b/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsScreen.kt @@ -36,7 +36,7 @@ import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent import com.google.samples.apps.nowinandroid.feature.interests.api.R @Composable -fun InterestsRoute( +fun InterestsScreen( onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, shouldHighlightSelectedTopic: Boolean = false, diff --git a/feature/interests/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/impl/InterestsListDetailScreenTest.kt b/feature/interests/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/impl/InterestsListDetailScreenTest.kt index 45d3e8507..1f2b039b9 100644 --- a/feature/interests/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/impl/InterestsListDetailScreenTest.kt +++ b/feature/interests/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/impl/InterestsListDetailScreenTest.kt @@ -29,6 +29,7 @@ import androidx.test.espresso.Espresso import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.model.data.Topic +import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsListDetailScreen import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest @@ -86,7 +87,7 @@ class InterestsListDetailScreenTest { composeTestRule.apply { setContent { NiaTheme { - com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsListDetailScreen() + InterestsListDetailScreen() } } @@ -101,7 +102,7 @@ class InterestsListDetailScreenTest { composeTestRule.apply { setContent { NiaTheme { - com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsListDetailScreen() + InterestsListDetailScreen() } } @@ -116,7 +117,7 @@ class InterestsListDetailScreenTest { composeTestRule.apply { setContent { NiaTheme { - com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsListDetailScreen() + InterestsListDetailScreen() } } @@ -135,7 +136,7 @@ class InterestsListDetailScreenTest { composeTestRule.apply { setContent { NiaTheme { - com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsListDetailScreen() + InterestsListDetailScreen() } } @@ -160,7 +161,7 @@ class InterestsListDetailScreenTest { BackHandler { unhandledBackPress = true } - com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsListDetailScreen() + InterestsListDetailScreen() } } @@ -180,7 +181,7 @@ class InterestsListDetailScreenTest { composeTestRule.apply { setContent { NiaTheme { - com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsListDetailScreen() + InterestsListDetailScreen() } } diff --git a/feature/search/api/build.gradle.kts b/feature/search/api/build.gradle.kts index 771cac5ca..8b0bcb138 100644 --- a/feature/search/api/build.gradle.kts +++ b/feature/search/api/build.gradle.kts @@ -25,7 +25,7 @@ android { dependencies { implementation(projects.core.data) implementation(projects.core.domain) - implementation(projects.core.navigation) + api(projects.core.navigation) testImplementation(projects.core.testing) diff --git a/feature/search/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/api/navigation/SearchNavigation.kt b/feature/search/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/api/navigation/SearchNavigation.kt index 0ddd81f27..24d6773db 100644 --- a/feature/search/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/api/navigation/SearchNavigation.kt +++ b/feature/search/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/api/navigation/SearchNavigation.kt @@ -20,16 +20,15 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey import kotlinx.serialization.Serializable @Serializable data object SearchRoute -@Serializable data class SearchRouteNav3(val onInterestsClick: () -> Unit) +@Serializable object SearchRouteNav3: NiaBackStackKey -fun NiaBackStack.navigateToSearch( - onInterestsClick: () -> Unit, -) { - navigate(SearchRouteNav3(onInterestsClick)) +fun NiaBackStack.navigateToSearch() { + navigate(SearchRouteNav3) } fun NavController.navigateToSearch(navOptions: NavOptions? = null) = diff --git a/feature/search/impl/build.gradle.kts b/feature/search/impl/build.gradle.kts index 9c189c03f..1b9499af5 100644 --- a/feature/search/impl/build.gradle.kts +++ b/feature/search/impl/build.gradle.kts @@ -27,9 +27,9 @@ android { dependencies { implementation(projects.core.data) implementation(projects.core.domain) - implementation(projects.core.navigation) implementation(projects.feature.interests.api) implementation(projects.feature.search.api) + implementation(projects.feature.topic.api) testImplementation(projects.core.testing) diff --git a/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchScreen.kt b/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchScreen.kt index c530b274a..6121e452b 100644 --- a/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchScreen.kt +++ b/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchScreen.kt @@ -93,14 +93,10 @@ import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success import com.google.samples.apps.nowinandroid.core.ui.R.string import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent import com.google.samples.apps.nowinandroid.core.ui.newsFeed -import com.google.samples.apps.nowinandroid.feature.search.impl.RecentSearchQueriesUiState.Loading -import com.google.samples.apps.nowinandroid.feature.search.impl.SearchResultUiState.EmptyQuery -import com.google.samples.apps.nowinandroid.feature.search.impl.SearchResultUiState.LoadFailed -import com.google.samples.apps.nowinandroid.feature.search.impl.SearchResultUiState.SearchNotReady import com.google.samples.apps.nowinandroid.feature.search.api.R as searchR @Composable -internal fun SearchRoute( +internal fun SearchScreen( onBackClick: () -> Unit, onInterestsClick: () -> Unit, onTopicClick: (String) -> Unit, @@ -131,7 +127,7 @@ internal fun SearchRoute( internal fun SearchScreen( modifier: Modifier = Modifier, searchQuery: String = "", - recentSearchesUiState: RecentSearchQueriesUiState = Loading, + recentSearchesUiState: RecentSearchQueriesUiState = RecentSearchQueriesUiState.Loading, searchResultUiState: SearchResultUiState = SearchResultUiState.Loading, onSearchQueryChanged: (String) -> Unit = {}, onSearchTriggered: (String) -> Unit = {}, @@ -154,11 +150,11 @@ internal fun SearchScreen( ) when (searchResultUiState) { SearchResultUiState.Loading, - LoadFailed, + SearchResultUiState.LoadFailed, -> Unit - SearchNotReady -> SearchNotReadyBody() - EmptyQuery, + SearchResultUiState.SearchNotReady -> SearchNotReadyBody() + SearchResultUiState.EmptyQuery, -> { if (recentSearchesUiState is RecentSearchQueriesUiState.Success) { RecentSearchesBody( diff --git a/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchUiStatePreviewParameterProvider.kt b/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchUiStatePreviewParameterProvider.kt index 680c97d41..d2963fc7f 100644 --- a/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchUiStatePreviewParameterProvider.kt +++ b/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchUiStatePreviewParameterProvider.kt @@ -22,7 +22,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.newsResources import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.topics -import com.google.samples.apps.nowinandroid.feature.search.impl.SearchResultUiState.Success /** * This [PreviewParameterProvider](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/PreviewParameterProvider) @@ -30,7 +29,7 @@ import com.google.samples.apps.nowinandroid.feature.search.impl.SearchResultUiSt */ class SearchUiStatePreviewParameterProvider : PreviewParameterProvider { override val values: Sequence = sequenceOf( - Success( + SearchResultUiState.Success( topics = topics.mapIndexed { i, topic -> FollowableTopic(topic = topic, isFollowed = i % 2 == 0) }, diff --git a/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModel.kt b/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModel.kt index 99e403d1a..13628de70 100644 --- a/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModel.kt +++ b/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModel.kt @@ -28,11 +28,6 @@ import com.google.samples.apps.nowinandroid.core.data.repository.UserDataReposit import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUseCase import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase import com.google.samples.apps.nowinandroid.core.model.data.UserSearchResult -import com.google.samples.apps.nowinandroid.feature.search.impl.SearchResultUiState.EmptyQuery -import com.google.samples.apps.nowinandroid.feature.search.impl.SearchResultUiState.LoadFailed -import com.google.samples.apps.nowinandroid.feature.search.impl.SearchResultUiState.Loading -import com.google.samples.apps.nowinandroid.feature.search.impl.SearchResultUiState.SearchNotReady -import com.google.samples.apps.nowinandroid.feature.search.impl.SearchResultUiState.Success import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -61,29 +56,29 @@ class SearchViewModel @Inject constructor( searchContentsRepository.getSearchContentsCount() .flatMapLatest { totalCount -> if (totalCount < SEARCH_MIN_FTS_ENTITY_COUNT) { - flowOf(SearchNotReady) + flowOf(SearchResultUiState.SearchNotReady) } else { searchQuery.flatMapLatest { query -> if (query.trim().length < SEARCH_QUERY_MIN_LENGTH) { - flowOf(EmptyQuery) + flowOf(SearchResultUiState.EmptyQuery) } else { getSearchContentsUseCase(query) // Not using .asResult() here, because it emits Loading state every // time the user types a letter in the search box, which flickers the screen. .map { data -> - Success( + SearchResultUiState.Success( topics = data.topics, newsResources = data.newsResources, ) } - .catch { emit(LoadFailed) } + .catch { emit(SearchResultUiState.LoadFailed) } } } } }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), - initialValue = Loading, + initialValue = SearchResultUiState.Loading, ) val recentSearchQueriesUiState: StateFlow = diff --git a/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/navigation/SearchEntryProvider.kt b/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/navigation/SearchEntryProvider.kt index 2cab3139a..880e6abe4 100644 --- a/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/navigation/SearchEntryProvider.kt +++ b/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/navigation/SearchEntryProvider.kt @@ -16,13 +16,14 @@ package com.google.samples.apps.nowinandroid.feature.search.impl.navigation -import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.navigation3.runtime.EntryProviderBuilder import androidx.navigation3.runtime.entry import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack -import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.navigateToInterests +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey +import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute import com.google.samples.apps.nowinandroid.feature.search.api.navigation.SearchRouteNav3 -import com.google.samples.apps.nowinandroid.feature.search.impl.SearchRoute +import com.google.samples.apps.nowinandroid.feature.search.impl.SearchScreen +import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -37,12 +38,12 @@ object SearchModule { @IntoSet fun provideSearchEntryProviderBuilder( backStack: NiaBackStack, - ): EntryProviderBuilder.() -> @JvmSuppressWildcards Unit = { + ): EntryProviderBuilder.() -> Unit = { entry { key -> - SearchRoute( + SearchScreen( onBackClick = backStack::removeLast, - onInterestsClick = key.onInterestsClick, - onTopicClick = backStack::navigateToInterests, + onInterestsClick = { backStack.navigateToTopLevelDestination(InterestsRoute()) }, + onTopicClick = backStack::navigateToTopic, ) } } diff --git a/feature/search/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModelTest.kt b/feature/search/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModelTest.kt index 8d86f38ce..a1f089b99 100644 --- a/feature/search/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModelTest.kt +++ b/feature/search/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModelTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2023 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. @@ -102,7 +102,7 @@ class SearchViewModelTest { searchContentsRepository.addTopics(topicsTestData) val result = viewModel.searchResultUiState.value - assertIs(result) + assertIs(result) } @Test diff --git a/feature/topic/api/build.gradle.kts b/feature/topic/api/build.gradle.kts index 7abbfea82..1ea6e54a8 100644 --- a/feature/topic/api/build.gradle.kts +++ b/feature/topic/api/build.gradle.kts @@ -24,10 +24,10 @@ android { } dependencies { + api(projects.core.navigation) implementation(projects.core.data) testImplementation(projects.core.testing) - implementation(projects.core.navigation) testImplementation(libs.robolectric) androidTestImplementation(libs.bundles.androidx.compose.ui.test) diff --git a/feature/topic/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/api/navigation/TopicNavigation.kt b/feature/topic/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/api/navigation/TopicNavigation.kt index 2594eda33..f1cf85864 100644 --- a/feature/topic/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/api/navigation/TopicNavigation.kt +++ b/feature/topic/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/api/navigation/TopicNavigation.kt @@ -23,11 +23,12 @@ import androidx.navigation.NavOptionsBuilder import androidx.navigation.compose.composable import androidx.navigation.toRoute import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStack +import com.google.samples.apps.nowinandroid.core.navigation.NiaBackStackKey import com.google.samples.apps.nowinandroid.feature.topic.api.TopicViewModel import com.google.samples.apps.nowinandroid.feature.topic.api.TopicScreen import kotlinx.serialization.Serializable -@Serializable data class TopicRoute(val id: String) +@Serializable data class TopicRoute(val id: String): NiaBackStackKey fun NiaBackStack.navigateToTopic( topicId: String, diff --git a/feature/topic/api/src/main/res/drawable/feature_topic_api_ic_topic_placeholder.xml b/feature/topic/api/src/main/res/drawable/feature_topic_api_ic_topic_placeholder.xml index 49975a008..39a10c38f 100644 --- a/feature/topic/api/src/main/res/drawable/feature_topic_api_ic_topic_placeholder.xml +++ b/feature/topic/api/src/main/res/drawable/feature_topic_api_ic_topic_placeholder.xml @@ -1,18 +1,18 @@ .() -> @JvmSuppressWildcards Unit = { - entry { key -> + ): EntryProviderBuilder.() -> Unit = { + entry( + metadata = ListDetailSceneStrategy.detailPane() + ) { key -> val id = key.id TopicScreen( showBackButton = true, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 250f2e28e..2d17bd351 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,6 +10,7 @@ androidxBrowser = "1.8.0" androidxComposeBom = "2025.02.00" androidxComposeFoundation = "1.8.0-alpha07" androidxComposeMaterial3Adaptive = "1.1.0-rc01" +androidxComposeMaterial3AdaptiveNavigation3 = "1.0.0-SNAPSHOT" androidxComposeRuntimeTracing = "1.7.6" androidxCore = "1.15.0" androidxCoreSplashscreen = "1.0.1" @@ -18,10 +19,11 @@ androidxEspresso = "3.6.1" androidxHiltNavigationCompose = "1.2.0" androidxLifecycle = "2.8.7" androidxLintGradle = "1.0.0-alpha03" +androidxLifecycleViewModelNavigation3 = "1.0.0-alpha03" androidxMacroBenchmark = "1.3.4" androidxMetrics = "1.0.0-beta01" androidxNavigation = "2.8.5" -androidxNavigation3 = "1.0.0-alpha03" +androidxNavigation3 = "1.0.0-alpha05" androidxProfileinstaller = "1.4.1" androidxTestCore = "1.7.0-rc01" androidxTestExt = "1.3.0-rc01" @@ -80,6 +82,7 @@ androidx-compose-material3-navigationSuite = { group = "androidx.compose.materia androidx-compose-material3-adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive", version.ref = "androidxComposeMaterial3Adaptive" } androidx-compose-material3-adaptive-layout = { group = "androidx.compose.material3.adaptive", name = "adaptive-layout", version.ref = "androidxComposeMaterial3Adaptive" } androidx-compose-material3-adaptive-navigation = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation", version.ref = "androidxComposeMaterial3Adaptive" } +androidx-compose-material3-adaptive-navigation3 = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation3",version.ref="androidxComposeMaterial3AdaptiveNavigation3" } androidx-compose-material3-windowSizeClass = { group = "androidx.compose.material3", name = "material3-window-size-class" } androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" } androidx-compose-runtime-tracing = { group = "androidx.compose.runtime", name = "runtime-tracing", version.ref = "androidxComposeRuntimeTracing" } @@ -96,6 +99,7 @@ androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navig androidx-lifecycle-runtimeCompose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } androidx-lifecycle-runtimeTesting = { group = "androidx.lifecycle", name = "lifecycle-runtime-testing", version.ref = "androidxLifecycle" } androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewModel-navigation3 = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-navigation3", version.ref = "androidxLifecycleViewModelNavigation3" } androidx-lint-gradle = { group = "androidx.lint", name = "lint-gradle", version.ref = "androidxLintGradle" } androidx-metrics = { group = "androidx.metrics", name = "metrics-performance", version.ref = "androidxMetrics" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 03adf46ee..c15c9e488 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -40,6 +40,9 @@ dependencyResolutionManagement { } } mavenCentral() + maven { + url = uri("https://androidx.dev/snapshots/builds/13764502/artifacts/repository") + } } } rootProject.name = "nowinandroid"