diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt index c0eba5fd3..a02490090 100644 --- a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt @@ -46,7 +46,6 @@ import dagger.hilt.android.testing.HiltAndroidTest import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import javax.inject.Inject @@ -255,9 +254,6 @@ class NavigationTest { } } - // TODO decide if backStack should preserve previous stacks when navigating back to home tab (ForYou) - // https://github.com/android/nowinandroid/issues/1937 - @Ignore @Test fun navigationBar_multipleBackStackInterests() { composeTestRule.apply { @@ -283,6 +279,32 @@ class NavigationTest { } } + @Test + fun navigationBar_backFromTabPreservesSubStack() { + composeTestRule.apply { + onNodeWithText(interests).performClick() + + // Select a topic + val topic = runBlocking { + topicsRepository.getTopics().first().sortedBy(Topic::name).last() + } + onNodeWithTag(LIST_PANE_TEST_TAG).performScrollToNode(hasText(topic.name)) + onNodeWithText(topic.name).performClick() + + // Verify the topic is shown + onNodeWithTag("topic:${topic.id}").assertIsDisplayed() + + // Switch to another tab + onNodeWithText(saved).performClick() + + // Press back to return to previous tab + Espresso.pressBack() + + // Verify the topic detail is still shown (sub-stack preserved) + onNodeWithTag("topic:${topic.id}").assertExists() + } + } + @Test fun navigatingToTopicFromForYou_showsTopicDetails() { composeTestRule.apply { 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 bfaa27fa6..38d29a9a7 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 @@ -16,6 +16,7 @@ package com.google.samples.apps.nowinandroid.ui +import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.WindowInsets @@ -264,8 +265,16 @@ internal fun NiaApp( searchEntry(navigator) } + val navigationState = appState.navigationState + BackHandler( + enabled = navigationState.currentKey == navigationState.currentTopLevelKey && + navigationState.currentTopLevelKey != navigationState.startKey, + ) { + navigator.goBack() + } + NavDisplay( - entries = appState.navigationState.toEntries(entryProvider), + entries = navigationState.toEntries(entryProvider), sceneStrategy = listDetailStrategy, onBack = { navigator.goBack() }, ) 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 864fec794..e0d8149f3 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 @@ -96,7 +96,5 @@ fun NavigationState.toEntries( ) } - return topLevelStack - .flatMap { decoratedEntries[it] ?: emptyList() } - .toMutableStateList() + return (decoratedEntries[currentTopLevelKey] ?: emptyList()).toMutableStateList() } 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 86c4acc25..9a2e7c44c 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 @@ -247,6 +247,32 @@ class NavigatorTest { assertThat(navigationState.currentTopLevelKey).isEqualTo(TestFirstTopLevelKey) } + @Test + fun testSubStackPreservedAfterTabSwitchAndBack() { + // Navigate to sub-page in first tab (ForYou → Topic) + navigator.navigate(TestKeyFirst) + + assertThat(navigationState.currentKey).isEqualTo(TestKeyFirst) + assertThat(navigationState.currentTopLevelKey).isEqualTo(TestFirstTopLevelKey) + + // Switch to second tab (e.g. Bookmarks) + navigator.navigate(TestSecondTopLevelKey) + + assertThat(navigationState.currentKey).isEqualTo(TestSecondTopLevelKey) + assertThat(navigationState.currentTopLevelKey).isEqualTo(TestSecondTopLevelKey) + + // Press back from second tab + navigator.goBack() + + // Should return to first tab with sub-stack preserved + assertThat(navigationState.currentTopLevelKey).isEqualTo(TestFirstTopLevelKey) + assertThat(navigationState.currentKey).isEqualTo(TestKeyFirst) + assertThat(navigationState.currentSubStack).containsExactly( + TestFirstTopLevelKey, + TestKeyFirst, + ).inOrder() + } + @Test fun throwOnEmptyBackStack() { assertFailsWith {