aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-01-21 02:44:14 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2022-01-21 02:44:14 +0000
commit515b8eb7bfb55623dc61a67f05e8534875bf02e5 (patch)
tree254eff23db490c1ef5dcc7fca55e8aff980c01a8
parentd5d0bcff7a16cd1093ca15f2f2fa5ac20ab9baad (diff)
parent28a2cf48e2d3fe5ad971f99852079894ee37af7e (diff)
downloadsupport-sparse-8101499-L91300000952726128.tar.gz
Merge "Merge cherrypicks of [1949864, 1953802, 1956576] into androidx-navigation-release." into androidx-navigation-releasesparse-8101499-L91300000952726128
-rw-r--r--navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.kt18
-rw-r--r--navigation/navigation-common/src/main/java/androidx/navigation/NavBackStackEntry.kt23
-rw-r--r--navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt2
-rw-r--r--navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavHostFragmentTest.kt4
-rw-r--r--navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.kt19
-rw-r--r--navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt40
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(),