Merge "GetBackStack to throw if route not in graph" into androidx-main
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
index 34c0450e..a308c35 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
@@ -1484,6 +1484,79 @@
@UiThreadTest
@Test
+ fun testGetBackStackEntryWithKClassNested() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ // a sibling graph with nested KClass destination
+ navigation<TestGraph>(startDestination = TestClass::class) {
+ test<TestClass>()
+ }
+ test("second")
+ }
+
+ navController.navigate(TestClass())
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo(
+ TEST_CLASS_ROUTE
+ )
+
+ navController.navigate("second")
+
+ assertThat(navigator.backStack.size).isEqualTo(3)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo("second")
+
+ val entry = navController.getBackStackEntry<TestClass>()
+ assertThat(entry.destination.route).isEqualTo(TEST_CLASS_ROUTE)
+ }
+
+ @UiThreadTest
+ @Test
+ fun testGetBackStackEntryWithKClassNotInGraph() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+ navController.navigate(TestClass())
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.getBackStackEntry<TestClassPathArg>()
+ }
+ assertThat(exception.message).isEqualTo(
+ "Destination with route TestClassPathArg cannot be found in " +
+ "navigation graph ${navController.graph}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
+ fun testGetBackStackEntryWithKClassNotInBackstack() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(1)
+
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.getBackStackEntry<TestClass>()
+ }
+ assertThat(exception.message).isEqualTo(
+ "No destination with route TestClass is on the NavController's " +
+ "back stack. The current destination is ${navController.currentDestination}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
fun testGetBackStackEntryWithObject() {
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
@@ -1561,6 +1634,79 @@
@UiThreadTest
@Test
+ fun testGetBackStackEntryWithObjectNested() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ // a sibling graph with nested KClass destination
+ navigation<TestGraph>(startDestination = TestClassPathArg::class) {
+ test<TestClassPathArg>()
+ }
+ test("second")
+ }
+
+ navController.navigate(TestClassPathArg(1))
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo(
+ TEST_CLASS_PATH_ARG_ROUTE
+ )
+
+ navController.navigate("second")
+
+ assertThat(navigator.backStack.size).isEqualTo(3)
+ assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo("second")
+
+ val entry = navController.getBackStackEntry(TestClassPathArg(1))
+ assertThat(entry.destination.route).isEqualTo(TEST_CLASS_PATH_ARG_ROUTE)
+ }
+
+ @UiThreadTest
+ @Test
+ fun testGetBackStackEntryWithObjectNotInGraph() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+ navController.navigate(TestClass())
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(2)
+
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.getBackStackEntry(TestClassPathArg(1))
+ }
+ assertThat(exception.message).isEqualTo(
+ "Destination with route TestClassPathArg cannot be found in " +
+ "navigation graph ${navController.graph}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
+ fun testGetBackStackEntryWithObjectNotInBackstack() {
+ val navController = createNavController()
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>()
+ }
+
+ val navigator = navController.navigatorProvider.getNavigator(TestNavigator::class.java)
+ assertThat(navigator.backStack.size).isEqualTo(1)
+
+ val exception = assertFailsWith<IllegalArgumentException> {
+ navController.getBackStackEntry(TestClass())
+ }
+ assertThat(exception.message).isEqualTo(
+ "No destination with route $TEST_CLASS_ROUTE is on the NavController's " +
+ "back stack. The current destination is ${navController.currentDestination}"
+ )
+ }
+
+ @UiThreadTest
+ @Test
fun testPopBackStack() {
val navController = createNavController()
navController.graph = nav_singleArg_graph
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
index c20f5fd..d0b7a2e 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -2699,8 +2699,21 @@
* target NavBackStackEntry's [NavDestination] must have been created with route from [KClass].
* @throws IllegalArgumentException if the destination is not on the back stack
*/
- public inline fun <reified T : Any> getBackStackEntry(): NavBackStackEntry =
- getBackStackEntry(serializer<T>().hashCode())
+ public inline fun <reified T : Any> getBackStackEntry(): NavBackStackEntry {
+ val id = serializer<T>().hashCode()
+ requireNotNull(findDestinationFromRoot(id)) {
+ "Destination with route ${T::class.simpleName} cannot be found in navigation " +
+ "graph $graph"
+ }
+ val lastFromBackStack = currentBackStack.value.lastOrNull { entry ->
+ entry.destination.id == id
+ }
+ requireNotNull(lastFromBackStack) {
+ "No destination with route ${T::class.simpleName} is on the NavController's " +
+ "back stack. The current destination is $currentDestination"
+ }
+ return lastFromBackStack
+ }
/**
* Gets the topmost [NavBackStackEntry] for a route from an Object.
@@ -2717,10 +2730,6 @@
// route contains arguments so we need to generate the populated route
// rather than getting entry based on route pattern
val finalRoute = generateRouteFilled(route)
- requireNotNull(finalRoute) {
- "No destination with route $finalRoute is on the NavController's back stack. The " +
- "current destination is $currentDestination"
- }
return getBackStackEntry(finalRoute)
}