From 3f9c56c1d742e38c5b329cec6e0a705f983a485c Mon Sep 17 00:00:00 2001 From: Jose Alcerreca Date: Mon, 11 Mar 2019 16:14:02 +0100 Subject: [PATCH] Adds optional case for item reselected Change-Id: Ib0fb38a10114e6b71cd57c1cda4c14cba209b538 --- .../BottomNavigationTest.kt | 18 ++++++ .../NavigationExtensions.kt | 60 ++++++++++++++----- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/NavigationAdvancedSample/app/src/androidTest/java/com/example/android/navigationadvancedsample/BottomNavigationTest.kt b/NavigationAdvancedSample/app/src/androidTest/java/com/example/android/navigationadvancedsample/BottomNavigationTest.kt index 8db54430..d3886906 100644 --- a/NavigationAdvancedSample/app/src/androidTest/java/com/example/android/navigationadvancedsample/BottomNavigationTest.kt +++ b/NavigationAdvancedSample/app/src/androidTest/java/com/example/android/navigationadvancedsample/BottomNavigationTest.kt @@ -113,6 +113,24 @@ class BottomNavigationTest { assertDeeperThirdScreen() } + @Test + fun bottomNavView_itemReselected_goesBackToStart() { + openThirdScreen() + + assertThirdScreen() + + onView(withContentDescription(R.string.sign_up)) + .perform(click()) + + assertDeeperThirdScreen() + + // Reselect the current item + openThirdScreen() + + // Verify that it popped the back stack until the start destination. + assertThirdScreen() + } + private fun assertSecondScreen() { onView(allOf(withText(R.string.title_list), isDescendantOfA(withId(R.id.action_bar)))) .check(matches(isDisplayed())) diff --git a/NavigationAdvancedSample/app/src/main/java/com/example/android/navigationadvancedsample/NavigationExtensions.kt b/NavigationAdvancedSample/app/src/main/java/com/example/android/navigationadvancedsample/NavigationExtensions.kt index 352d79f0..18168649 100644 --- a/NavigationAdvancedSample/app/src/main/java/com/example/android/navigationadvancedsample/NavigationExtensions.kt +++ b/NavigationAdvancedSample/app/src/main/java/com/example/android/navigationadvancedsample/NavigationExtensions.kt @@ -83,7 +83,7 @@ fun BottomNavigationView.setupWithNavController( val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId] var isOnFirstFragment = selectedItemTag == firstFragmentTag - // For the navigation item that is selected... + // When a navigation item is selected setOnNavigationItemSelectedListener { item -> // Don't do anything if the state is state has already been saved. if (fragmentManager.isStateSaved) { @@ -131,22 +131,11 @@ fun BottomNavigationView.setupWithNavController( } } - // handle deep link - navGraphIds.forEachIndexed { index, navGraphId -> - val fragmentTag = getFragmentTag(index) + // Optional: on item reselected, pop back stack to the destination of the graph + setupItemReselected(graphIdToTagMap, fragmentManager) - // Find or create the Navigation host fragment - val navHostFragment = obtainNavHostFragment( - fragmentManager, - fragmentTag, - navGraphId, - containerId - ) - // Handle Intent - if (navHostFragment.navController.handleDeepLink(intent)) { - this.selectedItemId = navHostFragment.navController.graph.id - } - } + // Handle deep link + setupDeepLinks(navGraphIds, fragmentManager, containerId, intent) // Finally, ensure that we update our BottomNavigationView when the back stack changes fragmentManager.addOnBackStackChangedListener { @@ -165,6 +154,45 @@ fun BottomNavigationView.setupWithNavController( return selectedNavController } +private fun BottomNavigationView.setupDeepLinks( + navGraphIds: List, + fragmentManager: FragmentManager, + containerId: Int, + intent: Intent +) { + navGraphIds.forEachIndexed { index, navGraphId -> + val fragmentTag = getFragmentTag(index) + + // Find or create the Navigation host fragment + val navHostFragment = obtainNavHostFragment( + fragmentManager, + fragmentTag, + navGraphId, + containerId + ) + // Handle Intent + if (navHostFragment.navController.handleDeepLink(intent)) { + this.selectedItemId = navHostFragment.navController.graph.id + } + } +} + +private fun BottomNavigationView.setupItemReselected( + graphIdToTagMap: SparseArray, + fragmentManager: FragmentManager +) { + setOnNavigationItemReselectedListener { item -> + val newlySelectedItemTag = graphIdToTagMap[item.itemId] + val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) + as NavHostFragment + val navController = selectedFragment.navController + // Pop the back stack to the start destination of the current navController graph + navController.popBackStack( + navController.graph.startDestination, false + ) + } +} + private fun detachNavHostFragment( fragmentManager: FragmentManager, navHostFragment: NavHostFragment