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 4438eee5e..ecc23d80e 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 @@ -75,7 +75,6 @@ class MainActivity : ComponentActivity() { private val viewModel: MainActivityViewModel by viewModels() - override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() super.onCreate(savedInstanceState) @@ -134,7 +133,6 @@ class MainActivity : ComponentActivity() { splashScreen.setKeepOnScreenCondition { viewModel.uiState.value.shouldKeepSplashScreen() } setContent { - val appState = rememberNiaAppState( networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelNavItem.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelNavItem.kt index c3a90219d..7021e4fa0 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelNavItem.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelNavItem.kt @@ -18,6 +18,14 @@ package com.google.samples.apps.nowinandroid.navigation 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.feature.bookmarks.api.navigation.BookmarksNavKey +import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouNavKey +import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsNavKey +import com.google.samples.apps.nowinandroid.feature.bookmarks.api.R as bookmarksR +import com.google.samples.apps.nowinandroid.feature.foryou.api.R as forYouR +import com.google.samples.apps.nowinandroid.feature.search.api.R as searchR /** * Type for the top level navigation items in the application. Contains UI information about the @@ -35,4 +43,31 @@ data class TopLevelNavItem( val unselectedIcon: ImageVector, @StringRes val iconTextId: Int, @StringRes val titleTextId: Int, -) \ No newline at end of file +) + +val FOR_YOU = TopLevelNavItem( + selectedIcon = NiaIcons.Upcoming, + unselectedIcon = NiaIcons.UpcomingBorder, + iconTextId = forYouR.string.feature_foryou_api_title, + titleTextId = R.string.app_name, +) + +val BOOKMARKS = TopLevelNavItem( + selectedIcon = NiaIcons.Bookmarks, + unselectedIcon = NiaIcons.BookmarksBorder, + iconTextId = bookmarksR.string.feature_bookmarks_api_title, + titleTextId = bookmarksR.string.feature_bookmarks_api_title, +) + +val INTERESTS = TopLevelNavItem( + selectedIcon = NiaIcons.Grid3x3, + unselectedIcon = NiaIcons.Grid3x3, + iconTextId = searchR.string.feature_search_api_interests, + titleTextId = searchR.string.feature_search_api_interests, +) + +val TOP_LEVEL_NAV_ITEMS = mapOf( + ForYouNavKey to FOR_YOU, + BookmarksNavKey to BOOKMARKS, + InterestsNavKey(null) to INTERESTS, +) diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelRoutes.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelRoutes.kt deleted file mode 100644 index 8c796a5a7..000000000 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelRoutes.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2022 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.google.samples.apps.nowinandroid.navigation - -import com.google.samples.apps.nowinandroid.R -import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons -import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksRoute -import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouRoute -import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute -import com.google.samples.apps.nowinandroid.feature.bookmarks.api.R as bookmarksR -import com.google.samples.apps.nowinandroid.feature.foryou.api.R as forYouR -import com.google.samples.apps.nowinandroid.feature.search.api.R as searchR - -val FOR_YOU = TopLevelNavItem( - selectedIcon = NiaIcons.Upcoming, - unselectedIcon = NiaIcons.UpcomingBorder, - iconTextId = forYouR.string.feature_foryou_api_title, - titleTextId = R.string.app_name, -) - -val BOOKMARKS = TopLevelNavItem( - selectedIcon = NiaIcons.Bookmarks, - unselectedIcon = NiaIcons.BookmarksBorder, - iconTextId = bookmarksR.string.feature_bookmarks_api_title, - titleTextId = bookmarksR.string.feature_bookmarks_api_title, -) - -val INTERESTS = TopLevelNavItem( - selectedIcon = NiaIcons.Grid3x3, - unselectedIcon = NiaIcons.Grid3x3, - iconTextId = searchR.string.feature_search_api_interests, - titleTextId = searchR.string.feature_search_api_interests, -) - -val TOP_LEVEL_NAV_ITEMS = mapOf( - ForYouRoute to FOR_YOU, - BookmarksRoute to BOOKMARKS, - InterestsRoute(null) to INTERESTS, -) 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 aaf57f452..16aefd455 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 @@ -76,13 +76,13 @@ import com.google.samples.apps.nowinandroid.core.navigation.Navigator import com.google.samples.apps.nowinandroid.core.navigation.toEntries import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.navigation.LocalSnackbarHostState import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.navigation.bookmarksEntry +import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouNavKey import com.google.samples.apps.nowinandroid.feature.foryou.impl.navigation.forYouEntry import com.google.samples.apps.nowinandroid.feature.interests.impl.navigation.interestsEntry -import com.google.samples.apps.nowinandroid.feature.search.api.navigation.SearchRoute +import com.google.samples.apps.nowinandroid.feature.search.api.navigation.SearchNavKey import com.google.samples.apps.nowinandroid.feature.search.impl.navigation.searchEntry import com.google.samples.apps.nowinandroid.feature.settings.api.SettingsDialog import com.google.samples.apps.nowinandroid.feature.topic.impl.navigation.topicEntry -import com.google.samples.apps.nowinandroid.navigation.FOR_YOU import com.google.samples.apps.nowinandroid.navigation.TOP_LEVEL_NAV_ITEMS import com.google.samples.apps.nowinandroid.feature.settings.api.R as settingsR @@ -92,7 +92,7 @@ fun NiaApp( modifier: Modifier = Modifier, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(), ) { - val shouldShowGradientBackground = appState.currentTopLevelNavItem == FOR_YOU + val shouldShowGradientBackground = appState.navigationState.currentTopLevelKey == ForYouNavKey var showSettingsDialog by rememberSaveable { mutableStateOf(false) } NiaBackground(modifier = modifier) { @@ -135,7 +135,8 @@ fun NiaApp( @Composable @OptIn( ExperimentalMaterial3Api::class, - ExperimentalComposeUiApi::class, ExperimentalMaterial3AdaptiveApi::class, + ExperimentalComposeUiApi::class, + ExperimentalMaterial3AdaptiveApi::class, ) internal fun NiaApp( appState: NiaAppState, @@ -145,7 +146,7 @@ internal fun NiaApp( modifier: Modifier = Modifier, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(), ) { - val unreadRoutes by appState.topLevelRoutesWithUnreadResources + val unreadNavKeys by appState.topLevelNavKeysWithUnreadResources .collectAsStateWithLifecycle() if (showSettingsDialog) { @@ -161,7 +162,7 @@ internal fun NiaApp( NiaNavigationSuiteScaffold( navigationSuiteItems = { TOP_LEVEL_NAV_ITEMS.forEach { (navKey, navItem) -> - val hasUnread = unreadRoutes.contains(navKey) + val hasUnread = unreadNavKeys.contains(navKey) val selected = navKey == appState.navigationState.currentTopLevelKey item( selected = selected, @@ -239,7 +240,7 @@ internal fun NiaApp( containerColor = Color.Transparent, ), onActionClick = { onTopAppBarActionClick() }, - onNavigationClick = { navigator.navigate(SearchRoute) }, + onNavigationClick = { navigator.navigate(SearchNavKey) }, ) } @@ -253,7 +254,6 @@ internal fun NiaApp( }, ), ) { - val listDetailStrategy = rememberListDetailSceneStrategy() val entryProvider = entryProvider { 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 a8921e967..9b4814980 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 @@ -26,10 +26,10 @@ import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.navigation.NavigationState import com.google.samples.apps.nowinandroid.core.navigation.rememberNavigationState -import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksRoute -import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouRoute +import com.google.samples.apps.nowinandroid.core.ui.TrackDisposableJank +import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksNavKey +import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouNavKey import com.google.samples.apps.nowinandroid.navigation.TOP_LEVEL_NAV_ITEMS -import com.google.samples.apps.nowinandroid.navigation.TopLevelNavItem import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -45,8 +45,7 @@ fun rememberNiaAppState( timeZoneMonitor: TimeZoneMonitor, coroutineScope: CoroutineScope = rememberCoroutineScope(), ): NiaAppState { - - val navigationState = rememberNavigationState(ForYouRoute, TOP_LEVEL_NAV_ITEMS.keys) + val navigationState = rememberNavigationState(ForYouNavKey, TOP_LEVEL_NAV_ITEMS.keys) NavigationTrackingSideEffect(navigationState) @@ -75,10 +74,6 @@ class NiaAppState( userNewsResourceRepository: UserNewsResourceRepository, timeZoneMonitor: TimeZoneMonitor, ) { - // TODO: I think this should return null if the current route is not a topLevelRoute - val currentTopLevelNavItem: TopLevelNavItem? - @Composable get() = TOP_LEVEL_NAV_ITEMS[navigationState.currentTopLevelKey] - val isOffline = networkMonitor.isOnline .map(Boolean::not) .stateIn( @@ -88,14 +83,14 @@ class NiaAppState( ) /** - * The top level routes that have unread news resources. + * The top level nav keys that have unread news resources. */ - val topLevelRoutesWithUnreadResources: StateFlow> = + val topLevelNavKeysWithUnreadResources: StateFlow> = userNewsResourceRepository.observeAllForFollowedTopics() .combine(userNewsResourceRepository.observeAllBookmarked()) { forYouNewsResources, bookmarkedNewsResources -> setOfNotNull( - ForYouRoute.takeIf { forYouNewsResources.any { !it.hasBeenViewed } }, - BookmarksRoute.takeIf { bookmarkedNewsResources.any { !it.hasBeenViewed } }, + ForYouNavKey.takeIf { forYouNewsResources.any { !it.hasBeenViewed } }, + BookmarksNavKey.takeIf { bookmarkedNewsResources.any { !it.hasBeenViewed } }, ) } .stateIn( @@ -115,15 +110,10 @@ class NiaAppState( /** * Stores information about navigation events to be used with JankStats */ - -// TODO: NavigationState needs to expose an observable representation of its state for this to work @Composable private fun NavigationTrackingSideEffect(navigationState: NavigationState) { -// TrackDisposableJank(niaNavigator) { metricsHolder -> -// snapshotFlow { -// val stack = niaNavigator.backStack.toList() -// metricsHolder.state?.putState("Navigation", stack.lastOrNull().toString()) -// } -// onDispose { } -// } + TrackDisposableJank(navigationState.currentKey) { metricsHolder -> + metricsHolder.state?.putState("Navigation", navigationState.currentKey.toString()) + onDispose {} + } } diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt index 44070dfc4..9c9488fde 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt @@ -125,11 +125,9 @@ class NiaAppScreenSizesScreenshotTests { networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, timeZoneMonitor = timeZoneMonitor, - niaNavigator = mockNiaBackStack(), ) NiaApp( fakeAppState, - entryProviderBuilders = MockEntryProvider, windowAdaptiveInfo = WindowAdaptiveInfo( windowSizeClass = WindowSizeClass.compute( width.value, diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt index 5a4726dd3..ede41fc19 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt @@ -18,14 +18,17 @@ package com.google.samples.apps.nowinandroid.ui import androidx.compose.runtime.remember import androidx.compose.ui.test.junit4.createComposeRule +import androidx.navigation3.runtime.NavBackStack import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.navigation.NavigationState +import com.google.samples.apps.nowinandroid.core.navigation.Navigator import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.util.TestNetworkMonitor import com.google.samples.apps.nowinandroid.core.testing.util.TestTimeZoneMonitor -import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksRoute -import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouRoute -import com.google.samples.apps.nowinandroid.navigation.TOP_LEVEL_NAV_ITEMS +import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksNavKey +import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouNavKey +import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsNavKey import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltTestApplication import kotlinx.coroutines.flow.collect @@ -39,7 +42,6 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import kotlin.test.assertEquals -import kotlin.test.assertTrue /** * Tests [NiaAppState]. @@ -63,31 +65,42 @@ class NiaAppStateTest { // Subject under test. private lateinit var state: NiaAppState + private fun testNavigationState() = NavigationState( + startKey = ForYouNavKey, + topLevelStack = NavBackStack(ForYouNavKey), + subStacks = mapOf( + ForYouNavKey to NavBackStack(ForYouNavKey), + BookmarksNavKey to NavBackStack(BookmarksNavKey), + ), + ) + @Test fun niaAppState_currentDestination() = runTest { - val niaBackStack = mockNiaBackStack() + val navigationState = testNavigationState() + val navigator = Navigator(navigationState) + composeTestRule.setContent { - state = remember(niaBackStack) { + state = remember(navigationState) { NiaAppState( - niaNavigator = niaBackStack, coroutineScope = backgroundScope, networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, timeZoneMonitor = timeZoneMonitor, + navigationState = navigationState, ) } } - assertEquals(ForYouRoute, state.niaNavigator.currentActiveTopLevelKey) - assertEquals(ForYouRoute, state.niaNavigator.currentKey) + assertEquals(ForYouNavKey, state.navigationState.currentTopLevelKey) + assertEquals(ForYouNavKey, state.navigationState.currentKey) // Navigate to another destination once - niaBackStack.navigate(BookmarksRoute) + navigator.navigate(BookmarksNavKey) composeTestRule.waitForIdle() - assertEquals(BookmarksRoute, state.niaNavigator.currentActiveTopLevelKey) - assertEquals(BookmarksRoute, state.niaNavigator.currentKey) + assertEquals(BookmarksNavKey, state.navigationState.currentTopLevelKey) + assertEquals(BookmarksNavKey, state.navigationState.currentKey) } @Test @@ -97,14 +110,16 @@ class NiaAppStateTest { networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, timeZoneMonitor = timeZoneMonitor, - niaNavigator = mockNiaBackStack(), ) } - assertEquals(3, TOP_LEVEL_NAV_ITEMS.size) - assertTrue(TOP_LEVEL_NAV_ITEMS[0].name.contains("for_you", true)) - assertTrue(TOP_LEVEL_NAV_ITEMS[1].name.contains("bookmarks", true)) - assertTrue(TOP_LEVEL_NAV_ITEMS[2].name.contains("interests", true)) + val navigationState = state.navigationState + + assertEquals(3, navigationState.topLevelKeys.size) + assertEquals( + setOf(ForYouNavKey, BookmarksNavKey, InterestsNavKey), + navigationState.topLevelKeys, + ) } @Test @@ -115,7 +130,7 @@ class NiaAppStateTest { networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, timeZoneMonitor = timeZoneMonitor, - niaNavigator = mockNiaBackStack(), + navigationState = testNavigationState(), ) } @@ -135,7 +150,7 @@ class NiaAppStateTest { networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, timeZoneMonitor = timeZoneMonitor, - niaNavigator = mockNiaBackStack(), + navigationState = testNavigationState(), ) } val changedTz = TimeZone.of("Europe/Prague") diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt index 29a470315..3c7610193 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt @@ -250,11 +250,9 @@ class SnackbarInsetsScreenshotTests { networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, timeZoneMonitor = timeZoneMonitor, - niaNavigator = mockNiaBackStack(), ) NiaApp( appState = appState, - entryProviderBuilders = MockEntryProvider, showSettingsDialog = false, onSettingsDismissed = {}, onTopAppBarActionClick = {}, diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt index 7e89184f4..75dc6baa7 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt @@ -200,11 +200,9 @@ class SnackbarScreenshotTests { networkMonitor = networkMonitor, userNewsResourceRepository = userNewsResourceRepository, timeZoneMonitor = timeZoneMonitor, - niaNavigator = mockNiaBackStack(), ) NiaApp( appState = appState, - entryProviderBuilders = MockEntryProvider, showSettingsDialog = false, onSettingsDismissed = {}, onTopAppBarActionClick = {}, diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/TestUtil.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/TestUtil.kt deleted file mode 100644 index 268cc8ee8..000000000 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/TestUtil.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2025 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.google.samples.apps.nowinandroid.ui - -import androidx.navigation3.runtime.EntryProviderScope -import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey -import com.google.samples.apps.nowinandroid.core.navigation.NiaNavigator -import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouRoute -import com.google.samples.apps.nowinandroid.feature.foryou.impl.ForYouScreen - -val MockEntryProvider: Set.() -> Unit> = - setOf( - { - entry { - ForYouScreen({}) - } - }, - ) - -private val startKey = ForYouRoute - -fun mockNiaBackStack() = NiaNavigator(startKey) diff --git a/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NavigationState.kt b/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NavigationState.kt index c870916db..2a508e6a9 100644 --- a/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NavigationState.kt +++ b/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NavigationState.kt @@ -36,9 +36,8 @@ import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator @Composable fun rememberNavigationState( startKey: NavKey, - topLevelKeys: Set + topLevelKeys: Set, ): NavigationState { - val topLevelStack = rememberNavBackStack(startKey) val subStacks = topLevelKeys.associateWith { key -> rememberNavBackStack(key) } @@ -46,7 +45,7 @@ fun rememberNavigationState( NavigationState( startKey = startKey, topLevelStack = topLevelStack, - subStacks = subStacks + subStacks = subStacks, ) } } @@ -61,7 +60,7 @@ fun rememberNavigationState( class NavigationState( val startKey: NavKey, val topLevelStack: NavBackStack, - val subStacks: Map> + val subStacks: Map>, ) { val currentTopLevelKey: NavKey by derivedStateOf { topLevelStack.last() } @@ -69,13 +68,12 @@ class NavigationState( get() = subStacks.keys @get:VisibleForTesting - val currentSubStack : NavBackStack + val currentSubStack: NavBackStack get() = subStacks[currentTopLevelKey] ?: error("Sub stack for $currentTopLevelKey does not exist") @get:VisibleForTesting - val currentKey - get() = currentSubStack.last() + val currentKey: NavKey by derivedStateOf { currentSubStack.last() } } /** @@ -83,9 +81,8 @@ class NavigationState( */ @Composable fun NavigationState.toEntries( - entryProvider: (NavKey) -> NavEntry + entryProvider: (NavKey) -> NavEntry, ): SnapshotStateList> { - val decoratedEntries = subStacks.mapValues { (_, stack) -> val decorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), @@ -93,11 +90,11 @@ fun NavigationState.toEntries( rememberDecoratedNavEntries( backStack = stack, entryDecorators = decorators, - entryProvider = entryProvider + entryProvider = entryProvider, ) } return topLevelStack .flatMap { decoratedEntries[it] ?: emptyList() } .toMutableStateList() -} \ No newline at end of file +} diff --git a/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/Navigator.kt b/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/Navigator.kt index 8851b0dca..e98e2c12b 100644 --- a/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/Navigator.kt +++ b/core/navigation/src/main/kotlin/com/google/samples/apps/nowinandroid/core/navigation/Navigator.kt @@ -42,7 +42,7 @@ class Navigator(val state: NavigationState) { * Go back to the previous navigation key. */ fun goBack() { - when (state.currentKey){ + when (state.currentKey) { state.startKey -> error("You cannot go back from the start route") state.currentTopLevelKey -> { // We're at the base of the current sub stack, go back to the previous top level @@ -88,4 +88,4 @@ class Navigator(val state: NavigationState) { if (size > 1) subList(1, size).clear() } } -} \ No newline at end of file +} diff --git a/core/navigation/src/test/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NavigatorTest.kt b/core/navigation/src/test/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NavigatorTest.kt index 05b6544d4..86c4acc25 100644 --- a/core/navigation/src/test/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NavigatorTest.kt +++ b/core/navigation/src/test/kotlin/com/google/samples/apps/nowinandroid/core/navigation/NavigatorTest.kt @@ -36,20 +36,19 @@ class NavigatorTest { @Before fun setup() { - val startKey = TestFirstTopLevelKey val topLevelStack = NavBackStack(startKey) val topLevelKeys = listOf( startKey, TestSecondTopLevelKey, - TestThirdTopLevelKey + TestThirdTopLevelKey, ) val subStacks = topLevelKeys.associateWith { key -> NavBackStack(key) } navigationState = NavigationState( startKey = startKey, topLevelStack = topLevelStack, - subStacks = subStacks + subStacks = subStacks, ) navigator = Navigator(navigationState) } @@ -66,7 +65,6 @@ class NavigatorTest { assertThat(navigationState.currentTopLevelKey).isEqualTo(TestFirstTopLevelKey) assertThat(navigationState.subStacks[TestFirstTopLevelKey]?.last()).isEqualTo(TestKeyFirst) - } @Test @@ -148,7 +146,7 @@ class NavigatorTest { assertThat(navigationState.currentKey).isEqualTo(TestKeyFirst) assertThat(navigationState.currentTopLevelKey).isEqualTo(TestFirstTopLevelKey) } - + @Test fun testPopOneNonTopLevel() { navigator.navigate(TestKeyFirst) @@ -256,5 +254,3 @@ class NavigatorTest { } } } - - diff --git a/docs/images/graphs/dep_graph_app.svg b/docs/images/graphs/dep_graph_app.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_app_nia_catalog.svg b/docs/images/graphs/dep_graph_app_nia_catalog.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_analytics.svg b/docs/images/graphs/dep_graph_core_analytics.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_common.svg b/docs/images/graphs/dep_graph_core_common.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_data.svg b/docs/images/graphs/dep_graph_core_data.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_data_test.svg b/docs/images/graphs/dep_graph_core_data_test.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_database.svg b/docs/images/graphs/dep_graph_core_database.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_datastore.svg b/docs/images/graphs/dep_graph_core_datastore.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_datastore_proto.svg b/docs/images/graphs/dep_graph_core_datastore_proto.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_datastore_test.svg b/docs/images/graphs/dep_graph_core_datastore_test.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_designsystem.svg b/docs/images/graphs/dep_graph_core_designsystem.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_domain.svg b/docs/images/graphs/dep_graph_core_domain.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_model.svg b/docs/images/graphs/dep_graph_core_model.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_navigation.svg b/docs/images/graphs/dep_graph_core_navigation.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_network.svg b/docs/images/graphs/dep_graph_core_network.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_notifications.svg b/docs/images/graphs/dep_graph_core_notifications.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_screenshot_testing.svg b/docs/images/graphs/dep_graph_core_screenshot_testing.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_testing.svg b/docs/images/graphs/dep_graph_core_testing.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_core_ui.svg b/docs/images/graphs/dep_graph_core_ui.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_bookmarks_api.svg b/docs/images/graphs/dep_graph_feature_bookmarks_api.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_bookmarks_impl.svg b/docs/images/graphs/dep_graph_feature_bookmarks_impl.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_foryou_api.svg b/docs/images/graphs/dep_graph_feature_foryou_api.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_foryou_impl.svg b/docs/images/graphs/dep_graph_feature_foryou_impl.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_interests_api.svg b/docs/images/graphs/dep_graph_feature_interests_api.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_interests_impl.svg b/docs/images/graphs/dep_graph_feature_interests_impl.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_search_api.svg b/docs/images/graphs/dep_graph_feature_search_api.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_search_impl.svg b/docs/images/graphs/dep_graph_feature_search_impl.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_settings_api.svg b/docs/images/graphs/dep_graph_feature_settings_api.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_topic_api.svg b/docs/images/graphs/dep_graph_feature_topic_api.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_feature_topic_impl.svg b/docs/images/graphs/dep_graph_feature_topic_impl.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_lint.svg b/docs/images/graphs/dep_graph_lint.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_sync_sync_test.svg b/docs/images/graphs/dep_graph_sync_sync_test.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_sync_work.svg b/docs/images/graphs/dep_graph_sync_work.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/images/graphs/dep_graph_ui_test_hilt_manifest.svg b/docs/images/graphs/dep_graph_ui_test_hilt_manifest.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/feature/bookmarks/api/README.md b/feature/bookmarks/api/README.md deleted file mode 100644 index 7ee6e7b86..000000000 --- a/feature/bookmarks/api/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# :feature:bookmarks:api module -## Dependency graph -![Dependency graph](../../../docs/images/graphs/dep_graph_feature_bookmarks_api.svg) diff --git a/feature/bookmarks/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/api/navigation/BookmarksRoute.kt b/feature/bookmarks/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/api/navigation/BookmarksNavKey.kt similarity index 96% rename from feature/bookmarks/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/api/navigation/BookmarksRoute.kt rename to feature/bookmarks/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/api/navigation/BookmarksNavKey.kt index 994aeecf1..988266ffc 100644 --- a/feature/bookmarks/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/api/navigation/BookmarksRoute.kt +++ b/feature/bookmarks/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/api/navigation/BookmarksNavKey.kt @@ -20,4 +20,4 @@ import androidx.navigation3.runtime.NavKey import kotlinx.serialization.Serializable @Serializable -object BookmarksRoute : NavKey +object BookmarksNavKey : NavKey diff --git a/feature/bookmarks/impl/README.md b/feature/bookmarks/impl/README.md deleted file mode 100644 index f7cc92060..000000000 --- a/feature/bookmarks/impl/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# :feature:bookmarks:impl module -## Dependency graph -![Dependency graph](../../../docs/images/graphs/dep_graph_feature_bookmarks_impl.svg) 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 9e9d25bf1..467965651 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,12 +23,12 @@ import androidx.compose.runtime.compositionLocalOf import androidx.navigation3.runtime.EntryProviderScope import androidx.navigation3.runtime.NavKey import com.google.samples.apps.nowinandroid.core.navigation.Navigator -import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksRoute +import com.google.samples.apps.nowinandroid.feature.bookmarks.api.navigation.BookmarksNavKey import com.google.samples.apps.nowinandroid.feature.bookmarks.impl.BookmarksScreen import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic fun EntryProviderScope.bookmarksEntry(navigator: Navigator) { - entry { + entry { val snackbarHostState = LocalSnackbarHostState.current BookmarksScreen( onTopicClick = navigator::navigateToTopic, @@ -43,6 +43,7 @@ fun EntryProviderScope.bookmarksEntry(navigator: Navigator) { } } +// TODO: Why is this here? val LocalSnackbarHostState = compositionLocalOf { error("SnackbarHostState state should be initialized at runtime") } diff --git a/feature/foryou/api/README.md b/feature/foryou/api/README.md deleted file mode 100644 index 2d3154ba2..000000000 --- a/feature/foryou/api/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# :feature:foryou:api module -## Dependency graph -![Dependency graph](../../../docs/images/graphs/dep_graph_feature_foryou_api.svg) diff --git a/feature/foryou/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/api/navigation/ForYouRoute.kt b/feature/foryou/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/api/navigation/ForYouNavKey.kt similarity index 96% rename from feature/foryou/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/api/navigation/ForYouRoute.kt rename to feature/foryou/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/api/navigation/ForYouNavKey.kt index 91adea3a1..d61ae5a20 100644 --- a/feature/foryou/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/api/navigation/ForYouRoute.kt +++ b/feature/foryou/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/api/navigation/ForYouNavKey.kt @@ -20,4 +20,4 @@ import androidx.navigation3.runtime.NavKey import kotlinx.serialization.Serializable @Serializable -object ForYouRoute : NavKey +object ForYouNavKey : NavKey diff --git a/feature/foryou/impl/README.md b/feature/foryou/impl/README.md deleted file mode 100644 index 32d4e0845..000000000 --- a/feature/foryou/impl/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# :feature:foryou:impl module -## Dependency graph -![Dependency graph](../../../docs/images/graphs/dep_graph_feature_foryou_impl.svg) diff --git a/feature/foryou/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/ForYouScreen.kt b/feature/foryou/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/ForYouScreen.kt index 907f50d6a..0ae916db3 100644 --- a/feature/foryou/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/ForYouScreen.kt +++ b/feature/foryou/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/ForYouScreen.kt @@ -20,7 +20,6 @@ import android.net.Uri import android.os.Build.VERSION import android.os.Build.VERSION_CODES import androidx.activity.compose.ReportDrawnWhen -import androidx.annotation.VisibleForTesting import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -107,8 +106,7 @@ import com.google.samples.apps.nowinandroid.core.ui.newsFeed import com.google.samples.apps.nowinandroid.feature.foryou.api.R @Composable -@VisibleForTesting -public fun ForYouScreen( +fun ForYouScreen( onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, viewModel: ForYouViewModel = hiltViewModel(), 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 d2aefa0f8..4bdc7368c 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,17 +19,14 @@ package com.google.samples.apps.nowinandroid.feature.foryou.impl.navigation import androidx.navigation3.runtime.EntryProviderScope import androidx.navigation3.runtime.NavKey import com.google.samples.apps.nowinandroid.core.navigation.Navigator -import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouRoute +import com.google.samples.apps.nowinandroid.feature.foryou.api.navigation.ForYouNavKey import com.google.samples.apps.nowinandroid.feature.foryou.impl.ForYouScreen import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic fun EntryProviderScope.forYouEntry(navigator: Navigator) { - entry { + entry { ForYouScreen( onTopicClick = navigator::navigateToTopic, ) } } - - - diff --git a/feature/foryou/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/ForYouScreenScreenshotTests.kt b/feature/foryou/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/ForYouScreenScreenshotTests.kt index ac4287625..d0d73860e 100644 --- a/feature/foryou/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/ForYouScreenScreenshotTests.kt +++ b/feature/foryou/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/impl/ForYouScreenScreenshotTests.kt @@ -50,7 +50,7 @@ import java.util.TimeZone */ @RunWith(RobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -@Config(application = HiltTestApplication::class, sdk = [35]) +@Config(application = HiltTestApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ForYouScreenScreenshotTests { diff --git a/feature/interests/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/api/navigation/InterestsRoute.kt b/feature/interests/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/api/navigation/InterestsNavKey.kt similarity index 97% rename from feature/interests/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/api/navigation/InterestsRoute.kt rename to feature/interests/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/api/navigation/InterestsNavKey.kt index ca7968778..cd6c631a2 100644 --- a/feature/interests/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/api/navigation/InterestsRoute.kt +++ b/feature/interests/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/api/navigation/InterestsNavKey.kt @@ -20,7 +20,7 @@ import androidx.navigation3.runtime.NavKey import kotlinx.serialization.Serializable @Serializable -data class InterestsRoute( +data class InterestsNavKey( // The ID of the topic which will be initially selected at this destination val initialTopicId: String? = null, ) : NavKey diff --git a/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsViewModel.kt b/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsViewModel.kt index 8f203d6fb..f79d79d09 100644 --- a/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsViewModel.kt +++ b/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/InterestsViewModel.kt @@ -23,7 +23,7 @@ import com.google.samples.apps.nowinandroid.core.data.repository.UserDataReposit import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.domain.TopicSortField import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic -import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute +import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsNavKey import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -40,7 +40,7 @@ class InterestsViewModel @AssistedInject constructor( val userDataRepository: UserDataRepository, getFollowableTopics: GetFollowableTopicsUseCase, // TODO: see comment below - @Assisted val key: InterestsRoute, + @Assisted val key: InterestsNavKey, ) : ViewModel() { // TODO: this should no longer be necessary, the currently selected topic should be @@ -77,7 +77,7 @@ class InterestsViewModel @AssistedInject constructor( @AssistedFactory interface Factory { - fun create(key: InterestsRoute): InterestsViewModel + fun create(key: InterestsNavKey): InterestsViewModel } } diff --git a/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/navigation/InterestsEntryProvider.kt b/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/navigation/InterestsEntryProvider.kt index 4aa12ef86..b92f462eb 100644 --- a/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/navigation/InterestsEntryProvider.kt +++ b/feature/interests/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/impl/navigation/InterestsEntryProvider.kt @@ -22,34 +22,15 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.navigation3.runtime.EntryProviderScope import androidx.navigation3.runtime.NavKey import com.google.samples.apps.nowinandroid.core.navigation.Navigator -import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute +import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsNavKey import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsDetailPlaceholder import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsScreen import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsViewModel -import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.TopicRoute import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic -//import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityComponent -import dagger.multibindings.IntoSet - -@Module -@InstallIn(ActivityComponent::class) -object InterestsEntryProvider { - - @OptIn(ExperimentalMaterial3AdaptiveApi::class) - @Provides - @IntoSet - fun provideInterestsEntryProviderBuilder( - navigator: Navigator, - ): EntryProviderScope.() -> Unit = { interestsEntry(navigator) } -} @OptIn(ExperimentalMaterial3AdaptiveApi::class) fun EntryProviderScope.interestsEntry(navigator: Navigator) { - entry( + entry( metadata = ListDetailSceneStrategy.listPane { InterestsDetailPlaceholder() }, 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 da3abaa68..0975216d1 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 @@ -18,10 +18,10 @@ package com.google.samples.apps.nowinandroid.interests.impl -import androidx.activity.viewModels import androidx.annotation.StringRes import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi import androidx.compose.material3.adaptive.navigation3.rememberListDetailSceneStrategy +import androidx.compose.runtime.Composable import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.junit4.AndroidComposeTestRule @@ -29,35 +29,26 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick -import androidx.navigation3.runtime.EntryProviderScope import androidx.navigation3.runtime.entryProvider import androidx.navigation3.ui.NavDisplay 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.core.navigation.NiaBackStackViewModel -import com.google.samples.apps.nowinandroid.core.navigation.NiaNavKey -import com.google.samples.apps.nowinandroid.core.navigation.NiaNavigator +import com.google.samples.apps.nowinandroid.core.navigation.Navigator +import com.google.samples.apps.nowinandroid.core.navigation.rememberNavigationState +import com.google.samples.apps.nowinandroid.core.navigation.toEntries import com.google.samples.apps.nowinandroid.feature.interests.api.R -import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute +import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsNavKey import com.google.samples.apps.nowinandroid.feature.interests.impl.LIST_PANE_TEST_TAG +import com.google.samples.apps.nowinandroid.feature.interests.impl.navigation.interestsEntry +import com.google.samples.apps.nowinandroid.feature.topic.impl.navigation.topicEntry import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity -import dagger.Module -import dagger.Provides -import dagger.hilt.EntryPoint -import dagger.hilt.EntryPoints -import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityComponent import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltTestApplication -import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking -import kotlinx.serialization.modules.PolymorphicModuleBuilder -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.polymorphic import org.junit.Before import org.junit.Rule import org.junit.Test @@ -65,7 +56,6 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import javax.inject.Inject -import javax.inject.Singleton import kotlin.getValue import kotlin.properties.ReadOnlyProperty @@ -83,13 +73,6 @@ class InterestsListDetailScreenTest { @get:Rule(order = 1) val composeTestRule = createAndroidComposeRule() - // entry point to get the features' hilt-injected EntryProviders that are installed in ActivityComponent - @EntryPoint - @InstallIn(ActivityComponent::class) - interface EntryProvidersEntryPoint { - fun getEntryProviders(): Set<@JvmSuppressWildcards EntryProviderScope.() -> Unit> - } - @Inject lateinit var topicsRepository: TopicsRepository @@ -104,15 +87,9 @@ class InterestsListDetailScreenTest { private val Topic.testTag get() = "topic:${this.id}" - private lateinit var entryProviderBuilders: Set.() -> Unit> - @Before fun setup() { hiltRule.inject() - composeTestRule.apply { - entryProviderBuilders = EntryPoints.get(activity, EntryProvidersEntryPoint::class.java) - .getEntryProviders() - } } @Test @@ -121,13 +98,7 @@ class InterestsListDetailScreenTest { composeTestRule.apply { setContent { NiaTheme { - NavDisplay( - backStack = listOf(InterestsRoute()), - sceneStrategy = rememberListDetailSceneStrategy(), - entryProvider = entryProvider { - entryProviderBuilders.forEach { it() } - }, - ) + TestNavDisplay() } } onNodeWithTag(LIST_PANE_TEST_TAG).assertIsDisplayed() @@ -135,19 +106,36 @@ class InterestsListDetailScreenTest { } } + @Composable + private fun TestNavDisplay() { + val startKey = InterestsNavKey(null) + + val navigationState = rememberNavigationState( + startKey = startKey, + topLevelKeys = setOf(startKey), + ) + + val navigator = Navigator(navigationState) + + val entryProvider = entryProvider { + interestsEntry(navigator) + topicEntry(navigator) + } + + NavDisplay( + entries = navigationState.toEntries(entryProvider), + onBack = { navigator.goBack() }, + sceneStrategy = rememberListDetailSceneStrategy(), + ) + } + @Test @Config(qualifiers = COMPACT_WIDTH) fun compactWidth_initialState_showsListPane() { composeTestRule.apply { setContent { NiaTheme { - NavDisplay( - backStack = listOf(InterestsRoute()), - sceneStrategy = rememberListDetailSceneStrategy(), - entryProvider = entryProvider { - entryProviderBuilders.forEach { it() } - }, - ) + TestNavDisplay() } } @@ -161,17 +149,8 @@ class InterestsListDetailScreenTest { fun expandedWidth_topicSelected_updatesDetailPane() { composeTestRule.apply { setContent { - val backStackViewModel by composeTestRule.activity.viewModels() - // TODO: This is broken - val backStack = backStackViewModel.niaNavigator.backStack NiaTheme { - NavDisplay( - backStack = backStack, - sceneStrategy = rememberListDetailSceneStrategy(), - entryProvider = entryProvider { - entryProviderBuilders.forEach { it() } - }, - ) + TestNavDisplay() } } val firstTopic = getTopics().first() @@ -189,16 +168,8 @@ class InterestsListDetailScreenTest { fun compactWidth_topicSelected_showsTopicDetailPane() { composeTestRule.apply { setContent { - val backStackViewModel by composeTestRule.activity.viewModels() - val backStack = backStackViewModel.niaNavigator.backStack NiaTheme { - NavDisplay( - backStack = backStack, - sceneStrategy = rememberListDetailSceneStrategy(), - entryProvider = entryProvider { - entryProviderBuilders.forEach { it() } - }, - ) + TestNavDisplay() } } @@ -216,16 +187,8 @@ class InterestsListDetailScreenTest { fun compactWidth_backPressFromTopicDetail_showsListPane() { composeTestRule.apply { setContent { - val backStackViewModel by composeTestRule.activity.viewModels() - val backStack = backStackViewModel.niaNavigator.backStack NiaTheme { - NavDisplay( - backStack = backStack, - sceneStrategy = rememberListDetailSceneStrategy(), - entryProvider = entryProvider { - entryProviderBuilders.forEach { it() } - }, - ) + TestNavDisplay() } } @@ -246,22 +209,3 @@ private fun AndroidComposeTestRule<*, *>.stringResource( @StringRes resId: Int, ): ReadOnlyProperty = ReadOnlyProperty { _, _ -> activity.getString(resId) } - -@Module -@InstallIn(SingletonComponent::class) -object BackStackProvider { - @Provides - @Singleton - fun provideNiaBackStack(): NiaNavigator = - NiaNavigator(startKey = InterestsRoute()) - - @Provides - @Singleton - fun provideSerializersModule( - polymorphicModuleBuilders: Set<@JvmSuppressWildcards PolymorphicModuleBuilder.() -> Unit>, - ): SerializersModule = SerializersModule { - polymorphic(NiaNavKey::class) { - polymorphicModuleBuilders.forEach { it() } - } - } -} diff --git a/feature/interests/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/impl/InterestsViewModelTest.kt b/feature/interests/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/impl/InterestsViewModelTest.kt index 4998e86ca..4e964b52b 100644 --- a/feature/interests/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/impl/InterestsViewModelTest.kt +++ b/feature/interests/impl/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/impl/InterestsViewModelTest.kt @@ -24,7 +24,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule -import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute +import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsNavKey import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsUiState import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsViewModel import kotlinx.coroutines.flow.collect @@ -68,11 +68,11 @@ class InterestsViewModelTest { fun setup() { viewModel = InterestsViewModel( savedStateHandle = SavedStateHandle( - route = InterestsRoute(initialTopicId = testInputTopics[0].topic.id), + route = InterestsNavKey(initialTopicId = testInputTopics[0].topic.id), ), userDataRepository = userDataRepository, getFollowableTopics = getFollowableTopicsUseCase, - InterestsRoute(initialTopicId = testInputTopics[0].topic.id), + InterestsNavKey(initialTopicId = testInputTopics[0].topic.id), ) } diff --git a/feature/search/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/api/navigation/SearchRoute.kt b/feature/search/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/api/navigation/SearchNavKey.kt similarity index 96% rename from feature/search/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/api/navigation/SearchRoute.kt rename to feature/search/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/api/navigation/SearchNavKey.kt index 5007dbd9d..9588a8404 100644 --- a/feature/search/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/api/navigation/SearchRoute.kt +++ b/feature/search/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/api/navigation/SearchNavKey.kt @@ -20,4 +20,4 @@ import androidx.navigation3.runtime.NavKey import kotlinx.serialization.Serializable @Serializable -object SearchRoute : NavKey +object SearchNavKey : NavKey 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 d2963fc7f..1bda620c6 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 @@ -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. 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 f4df9f34a..4e3baf343 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 @@ -19,16 +19,16 @@ package com.google.samples.apps.nowinandroid.feature.search.impl.navigation import androidx.navigation3.runtime.EntryProviderScope import androidx.navigation3.runtime.NavKey import com.google.samples.apps.nowinandroid.core.navigation.Navigator -import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsRoute -import com.google.samples.apps.nowinandroid.feature.search.api.navigation.SearchRoute +import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsNavKey +import com.google.samples.apps.nowinandroid.feature.search.api.navigation.SearchNavKey import com.google.samples.apps.nowinandroid.feature.search.impl.SearchScreen import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic fun EntryProviderScope.searchEntry(navigator: Navigator) { - entry { + entry { SearchScreen( onBackClick = { navigator.goBack() }, - onInterestsClick = { navigator.navigate(InterestsRoute()) }, + onInterestsClick = { navigator.navigate(InterestsNavKey()) }, onTopicClick = navigator::navigateToTopic, ) } diff --git a/feature/topic/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/api/navigation/TopicRoute.kt b/feature/topic/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/api/navigation/TopicNavKey.kt similarity index 91% rename from feature/topic/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/api/navigation/TopicRoute.kt rename to feature/topic/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/api/navigation/TopicNavKey.kt index ebc6636e9..eb10b0cdd 100644 --- a/feature/topic/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/api/navigation/TopicRoute.kt +++ b/feature/topic/api/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/api/navigation/TopicNavKey.kt @@ -21,10 +21,10 @@ import com.google.samples.apps.nowinandroid.core.navigation.Navigator import kotlinx.serialization.Serializable @Serializable -data class TopicRoute(val id: String) : NavKey +data class TopicNavKey(val id: String) : NavKey fun Navigator.navigateToTopic( topicId: String, ) { - navigate(TopicRoute(topicId)) + navigate(TopicNavKey(topicId)) } diff --git a/feature/topic/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/impl/navigation/TopicEntryProvider.kt b/feature/topic/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/impl/navigation/TopicEntryProvider.kt index c49fdddfc..b091ca701 100644 --- a/feature/topic/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/impl/navigation/TopicEntryProvider.kt +++ b/feature/topic/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/impl/navigation/TopicEntryProvider.kt @@ -22,7 +22,7 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.navigation3.runtime.EntryProviderScope import androidx.navigation3.runtime.NavKey import com.google.samples.apps.nowinandroid.core.navigation.Navigator -import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.TopicRoute +import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.TopicNavKey import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.navigateToTopic import com.google.samples.apps.nowinandroid.feature.topic.impl.TopicScreen import com.google.samples.apps.nowinandroid.feature.topic.impl.TopicViewModel @@ -30,7 +30,7 @@ import com.google.samples.apps.nowinandroid.feature.topic.impl.TopicViewModel.Fa @OptIn(ExperimentalMaterial3AdaptiveApi::class) fun EntryProviderScope.topicEntry(navigator: Navigator) { - entry( + entry( metadata = ListDetailSceneStrategy.detailPane(), ) { key -> val id = key.id diff --git a/settings.gradle.kts b/settings.gradle.kts index fafb2b696..03adf46ee 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -40,9 +40,6 @@ dependencyResolutionManagement { } } mavenCentral() - maven { - url = uri("https://androidx.dev/snapshots/builds/14161874/artifacts/repository") - } } } rootProject.name = "nowinandroid"