diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-01-21 02:44:14 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2022-01-21 02:44:14 +0000 |
commit | 515b8eb7bfb55623dc61a67f05e8534875bf02e5 (patch) | |
tree | 254eff23db490c1ef5dcc7fca55e8aff980c01a8 | |
parent | d5d0bcff7a16cd1093ca15f2f2fa5ac20ab9baad (diff) | |
parent | 28a2cf48e2d3fe5ad971f99852079894ee37af7e (diff) | |
download | support-sparse-8101499-L91300000952726128.tar.gz |
Merge "Merge cherrypicks of [1949864, 1953802, 1956576] into androidx-navigation-release." into androidx-navigation-releasesparse-8101499-L91300000952726128
6 files changed, 91 insertions, 15 deletions
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.kt index 64e823d94d8..aa9ba24e07b 100644 --- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.kt +++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.kt @@ -1160,6 +1160,24 @@ class NavDeepLinkTest { } @Test + fun ensureValueIsDecodedProperly() { + val deepLinkString = "$DEEP_LINK_EXACT_HTTPS/users?myarg={myarg}" + val deepLink = NavDeepLink(deepLinkString) + + val value = "%555" + val matchArgs = deepLink.getMatchingArguments( + Uri.parse(deepLinkString.replace("{myarg}", Uri.encode(value))), + mapOf("myarg" to nullableStringArgument()) + ) + assertWithMessage("Args should not be null") + .that(matchArgs) + .isNotNull() + assertWithMessage("Args should contain the value without additional decoding") + .that(matchArgs?.getString("myarg")) + .isEqualTo(value) + } + + @Test fun deepLinkMissingRequiredArgument() { val deepLinkString = "$DEEP_LINK_EXACT_HTTPS/greeting?title={title}&text={text}" val deepLink = NavDeepLink(deepLinkString) diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt index 144263f0500..32159a712f6 100644 --- a/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt +++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt @@ -107,6 +107,7 @@ public class NavBackStackEntry private constructor( private var lifecycle = LifecycleRegistry(this) private val savedStateRegistryController = SavedStateRegistryController.create(this) + private var savedStateRegistryRestored = false private val defaultFactory by lazy { SavedStateViewModelFactory((context?.applicationContext as? Application), this, arguments) } @@ -115,11 +116,15 @@ public class NavBackStackEntry private constructor( * The [SavedStateHandle] for this entry. */ public val savedStateHandle: SavedStateHandle by lazy { - check(lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) { + check(savedStateRegistryRestored) { "You cannot access the NavBackStackEntry's SavedStateHandle until it is added to " + "the NavController's back stack (i.e., the Lifecycle of the NavBackStackEntry " + "reaches the CREATED state)." } + check(lifecycle.currentState != Lifecycle.State.DESTROYED) { + "You cannot access the NavBackStackEntry's SavedStateHandle after the " + + "NavBackStackEntry is destroyed." + } ViewModelProvider( this, NavResultSavedStateFactory(this, null) ).get(SavedStateViewModel::class.java).handle @@ -141,10 +146,6 @@ public class NavBackStackEntry private constructor( @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public var maxLifecycle: Lifecycle.State = Lifecycle.State.INITIALIZED set(maxState) { - if (field == Lifecycle.State.INITIALIZED) { - // Perform the restore just when moving from the INITIALIZED state - savedStateRegistryController.performRestore(savedState) - } field = maxState updateState() } @@ -162,6 +163,12 @@ public class NavBackStackEntry private constructor( */ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public fun updateState() { + if (!savedStateRegistryRestored) { + // Perform the restore just once, the first time updateState() is called + // and specifically *before* we move up the Lifecycle + savedStateRegistryController.performRestore(savedState) + savedStateRegistryRestored = true + } if (hostLifecycleState.ordinal < maxLifecycle.ordinal) { lifecycle.currentState = hostLifecycleState } else { @@ -177,11 +184,15 @@ public class NavBackStackEntry private constructor( * [androidx.navigation.NavHostController.setViewModelStore]. */ public override fun getViewModelStore(): ViewModelStore { - check(lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) { + check(savedStateRegistryRestored) { "You cannot access the NavBackStackEntry's ViewModels until it is added to " + "the NavController's back stack (i.e., the Lifecycle of the NavBackStackEntry " + "reaches the CREATED state)." } + check(lifecycle.currentState != Lifecycle.State.DESTROYED) { + "You cannot access the NavBackStackEntry's ViewModels after the " + + "NavBackStackEntry is destroyed." + } checkNotNull(viewModelStoreProvider) { "You must call setViewModelStore() on your NavHostController before accessing the " + "ViewModelStore of a navigation graph." diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt index f2ddc112e87..3287b4ee1d9 100644 --- a/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt +++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt @@ -190,7 +190,7 @@ public class NavDeepLink internal constructor( for (index in 0 until storedParam!!.size()) { var value: String? = null if (argMatcher != null) { - value = Uri.decode(argMatcher.group(index + 1)) + value = argMatcher.group(index + 1) } val argName = storedParam.getArgumentName(index) val argument = arguments[argName] diff --git a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavHostFragmentTest.kt b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavHostFragmentTest.kt index 919bceeb6ad..76400971091 100644 --- a/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavHostFragmentTest.kt +++ b/navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavHostFragmentTest.kt @@ -161,5 +161,9 @@ class NavControllerInOnCreateFragment : EmptyFragment() { assertWithMessage("The NavController's graph should be set") .that(navController.graph) .isNotNull() + val backStackEntry = navController.getBackStackEntry(R.id.start_fragment) + val savedStateHandle = backStackEntry.savedStateHandle + assertThat(savedStateHandle) + .isNotNull() } }
\ No newline at end of file diff --git a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.kt b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.kt index 587b598cb62..081ef2982d5 100644 --- a/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.kt +++ b/navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.kt @@ -16,6 +16,7 @@ package androidx.navigation.fragment import android.content.Context +import android.content.ContextWrapper import android.os.Bundle import android.util.AttributeSet import android.view.LayoutInflater @@ -112,15 +113,19 @@ public open class NavHostFragment : Fragment(), NavHost { @CallSuper public override fun onCreate(savedInstanceState: Bundle?) { - val context = requireContext() + var context = requireContext() navHostController = NavHostController(context) navHostController!!.setLifecycleOwner(this) - if (context is OnBackPressedDispatcherOwner) { - navHostController!!.setOnBackPressedDispatcher( - (context as OnBackPressedDispatcherOwner).onBackPressedDispatcher - ) - // Otherwise, caller must register a dispatcher on the controller explicitly - // by overriding onCreateNavHostController() + while (context is ContextWrapper) { + if (context is OnBackPressedDispatcherOwner) { + navHostController!!.setOnBackPressedDispatcher( + (context as OnBackPressedDispatcherOwner).onBackPressedDispatcher + ) + // Otherwise, caller must register a dispatcher on the controller explicitly + // by overriding onCreateNavHostController() + break + } + context = context.baseContext } // Set the default state - this will be updated whenever // onPrimaryNavigationFragmentChanged() is called diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt index 9dee0e55569..ee7e4d511fd 100644 --- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt +++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt @@ -271,7 +271,7 @@ class NavBackStackEntryTest { @UiThreadTest @Test - fun testGetSavedStateHandleInitializedLifecycle() { + fun testGetSavedStateHandleBeforeUpdateState() { val entry = NavBackStackEntry.create( ApplicationProvider.getApplicationContext(), NavDestination(TestNavigator()), viewModelStoreProvider = NavControllerViewModel() @@ -295,6 +295,44 @@ class NavBackStackEntryTest { @UiThreadTest @Test + fun testGetSavedStateHandleInitializedLifecycle() { + val entry = NavBackStackEntry.create( + ApplicationProvider.getApplicationContext(), + NavDestination(TestNavigator()), viewModelStoreProvider = NavControllerViewModel() + ) + entry.updateState() + + assertThat(entry.savedStateHandle).isNotNull() + } + + @UiThreadTest + @Test + fun testGetSavedStateHandleDestroyedLifecycle() { + val entry = NavBackStackEntry.create( + ApplicationProvider.getApplicationContext(), + NavDestination(TestNavigator()), viewModelStoreProvider = NavControllerViewModel() + ) + entry.maxLifecycle = Lifecycle.State.CREATED + // Immediately destroy the NavBackStackEntry + entry.maxLifecycle = Lifecycle.State.DESTROYED + + try { + entry.savedStateHandle + fail( + "Attempting to get SavedStateHandle for back stack entry after " + + "moving the Lifecycle to DESTROYED set should throw IllegalStateException" + ) + } catch (e: IllegalStateException) { + assertThat(e) + .hasMessageThat().contains( + "You cannot access the NavBackStackEntry's SavedStateHandle after the " + + "NavBackStackEntry is destroyed." + ) + } + } + + @UiThreadTest + @Test fun testGetSavedStateHandleNoViewModelSet() { val entry = NavBackStackEntry.create( ApplicationProvider.getApplicationContext(), |