Status Update
Comments
il...@google.com <il...@google.com>
mi...@gmail.com <mi...@gmail.com> #2
this issue was created using original issue https://issuetracker.google.com/issues/389970341#comment2
.
but i am not sure that described text above is the same as in original issue:
also doesn't work
https://example.com/path/detail?token=123 in all versions.
pattern:
https://example.com/path/.*?token={id}
P.S. original issue also has test project https://issuetracker.google.com/389970341#attachment62385890
au...@gmail.com <au...@gmail.com> #3
It's essentially the same cause - navigation is prioritizing the wrong deeplink when they share the same action.
be...@gmail.com <be...@gmail.com> #4
Fixed internally and will be available in navigation-2.8.8
md...@gmail.com <md...@gmail.com> #5
Project: platform/frameworks/support
Branch: androidx-main
Author: Clara Fok <
Link:
Fix deeplink incorrectly matching when deep link fields don’t match perfectly
Expand for full commit details
Fix deeplink incorrectly matching when deep link fields don’t match perfectly
Navigation used to allow matching as long as one of three (uri / action / mimType) matches. However this is erroneous, as each field is a defined filter - a potential match should at the very least fulfill all filters.
This is fixed by applying these ground rules for deeplink matching:
1. The added NavDeepLink field’s nullability has to match the NavDeepLinkRequest field’s nullability. i.e. if deeplink.uri is null, request.uri must also be null
2. Any NavDeepLink non-null fields has to match exactly with NavDeepLinkRequest’s non-null fields
To elaborate, consider this scenario where
request:
“www.example.com/13”, action = “theAction”
potential matches:
1. www.example.com/{id}, action = "theAction"
2. www.differentUrl.com, action = "theAction"
When handling this request , even though deeplink 2 has a matching action, the differing url will rule it out as a potential match.
This fix also addresses a bug with wildcards where
request:
www.example.com/wildCardMatch?token=123, action = "theAction"
potential matches:
1. www.example.com/.*?token={id}, action = "theAction"
2. www.example.com?token={id}, action = "theAction"
Even though both deep links have a matching Action, deeplink 1 has a better Uri match, and so the request will now match with deeplink 1 instead of deeplink 2.
Test: ./gradlew navigation:navigation-common:cC
Test: ./gradlew navigation:navigation-runtime:cC
Bug: 395712033
Relnote: “NavDeepLink matching has been fixed where a deeplink and a deeplink request have to match exactly on uri, action, and mime. Matching is no longer allowed if only one or two fields match.“
Change-Id: I3b0295caa6324cc707d080856e88e62b4c3cd4d5
Files:
- M
navigation/navigation-common/src/androidInstrumentedTest/kotlin/androidx/navigation/NavGraphAndroidTest.kt
- M
navigation/navigation-common/src/androidMain/kotlin/androidx/navigation/NavDestination.android.kt
- M
navigation/navigation-common/src/commonMain/kotlin/androidx/navigation/NavDeepLink.kt
- M
navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/BaseNavControllerTest.kt
- M
navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavControllerRouteTest.kt
- M
navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavControllerTest.kt
- M
navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavInflaterTest.kt
- M
navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_deeplink.xml
- M
navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_simple.xml
Hash: 85ccd3f102d5d1da4bfc7ceba71425319a6f1c50
Date: Wed Feb 19 12:51:38 2025
il...@google.com <il...@google.com>
ap...@google.com <ap...@google.com> #6
I think this fix has broken some other deeplinks.
I have the following defined in my NavHost file:
composable<Screen.AttractionDetail>(deepLinks = listOf(navDeepLink<Screen.AttractionDetail>(basePath = "gocity:/attraction"))) ...
@Serializable
data class AttractionDetail(val id: String, val hideBookmark: Boolean = false) : Screen
In one of my UI tests I have this code:
deeplinkNavigator("gocity:/attraction/$attractionId")
With 2.8.7 this works fine but as soon as I upgrade to 2.8.8 the deeplink no longer opens the AttractionDetail screen.
I've experimented with changing the data class to be val hideBookmark: Boolean?
and to using uriPattern
instead of basePath
but I can't get this deeplink to work in 2.8.8
ap...@google.com <ap...@google.com> #7
@Serializable
sealed interface Screen {
@Serializable
data class AttractionDetail(val id: String, val hideBookmark: Boolean = false) : Screen
}
controller.graph =
controller.createGraph(startDestination = "start") {
...
composable<Screen.AttractionDetail> { deepLink(navDeepLink<Screen.AttractionDetail>("gocity:/attraction")) }
}
val request = NavDeepLinkRequest(Uri.parse("gocity:/attraction/13"), null, null)
val handled = controller.handleDeepLink(request)
Can you file a separate bug and attach a repro?
il...@google.com <il...@google.com> #8
I also had problem after updating to version 2.8.8.
Explicitly setting action
to Intent.ACTION_VIEW
in the Deep Link builder now may be required if your intent filter for deeplink schema has android.intent.action.VIEW
.
At least that's how I solved the issue that I got after using version 2.8.8.
See example:
Manifest:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="company"
android:host="app"
android:pathPattern="/.*" />
</intent-filter>
In broadcast receiver:
//...
val deepLinkUri = NavDeepLinks.progressDeepLink.uriPattern
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse(deepLinkUri) //
).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
`package` = context.packageName
}
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
)
//...
Definition of DeepLink using builder:
val progressDeepLink = navDeepLink {
uriPattern = "$BASE/${Path.PROGRESS}"
action = Intent.ACTION_VIEW
}
Compose Navigation Graph Builder:
navigation<LimitsDirection>(
deepLinks = listOf(NavDeepLinks.limitsDeepLink)
) {
LimitsScreen(viewModel = limitsViewModel)
}
I hope that helps you.
Also, may be this fix requires some update for documentation and/or guides
an...@gmail.com <an...@gmail.com> #11
Am I right in determining from that, that the starting fragment can only have it's title passed during initial creation[$1] or direct navigation[$2]?
Assuming I have a starting fragment labelled "A", and a second fragment somewhere in the graph labelled "B".
If I wish to change the title for the starting fragment "A" due to some action taken (e.g button press) on that ("A") fragment then I will need to find some other means of setting it, is this correct?
Same if I want the title on fragment "A" to change based on a choice made on fragment "B", but navigation back to "A" (from "B") occurs via the back/up button or popping from the nav stack?
At the moment I'm setting the title for my starting fragment through a navigation listener (addOnNavigatedListener), and observing a LiveData change from my view model, as doing only one or the other results in situations where the title is not set. This means that for the majority of the time the title is set at least twice [$3].
$1) Presumably any one of these is only called once during the app's active lifetime, likely in `[Main]Activity.onCreate`, is that correct? As I understand it, using this feature would also require some alteration to how navigation is set up in the layout (.xml) files, right?
* `NavHostFragment.create(R.navigation.graph, args)`, or
* `navController.setGraph(R.navigation.graph, args)`, or
* `navController.setGraph(navGraph, args)`
$2) `navController.navigate(action)`
$3) The title is set by
1. the navigation component to whatever is in the label, and
2. the onNavigated listener, and
3. the observer on the LiveData.
While it's light work this repetition in setting the title seems wasteful.
I cannot quite put my finger on it but it seems strange to me that the navigation source gets to set the title as opposed to the destination setting the title based on some internal state.To make this work for me I would have to ensure that every source a fragment can be navigated from knows how to correctly determine the expected title, as opposed to that determination only being made in one place (the destination fragment).
il...@google.com <il...@google.com> #12
If you need dynamic titles or more complicated logic, you should not use android:label in your graph and instead set the title yourself - NavigationUI only sets a title if the android:label exists.
an...@gmail.com <an...@gmail.com> #13
Thanks, I've removed the label from the fragment in the nav graph resource file. It was used as the default/fallback title until the title was set through the user's action.
se...@google.com <se...@google.com> #14
jo...@gmail.com <jo...@gmail.com> #15
<fragment
android:id="@+id/exampleFragment"
android:name="com.example.ExampleFragment"
android:label="{title}"
tools:layout="@layout/fragment_examplel">
<argument
android:name="title"
app:argType="string"/>
</fragment>
This works fine, but as soon as the activity/view gets destroyed (e.g. rotating the device) the title gets cleared. let me know if I'm missing something but I'm using the safe args direction and passing the text as getString(R.string.example_title).
il...@google.com <il...@google.com> #16
st...@gmail.com <st...@gmail.com> #17
something like this ?
<fragment
android:id="@+id/displayFragment"
android:name="com.commonsware.jetpack.sampler.nav.DisplayFragment"
android:label="{User.userName}" >
<argument
android:name="User"
app:argType="com.example.app.User"/>
</fragment>
il...@google.com <il...@google.com> #18
Re #17 - no, that's not supported, the only supported format is {argName}
which calls toString()
on the argument. You'd want to file a new feature request.
Description
The navigation components use the value in `android:label` to set the [toolbar] title depending on where in the navigation graph the use has navigated to. This works fine except I want to have the title reflect the category of things the screen is referring to, but the specific thing that the user is about to edit.
Normally I would have piece of code like `updateTitle` below on the activity and then call it from the fragment (onResume) with:
``` kotlin
updateTitle(getString(R.string.book_title, bookNumber))
```
but this does not work unless I also remove the `android:label` attribute from the nav_graph.xml.
Component used:
'android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha01'
'android.arch.navigation:navigation-ui-ktx:1.0.0-alpha01'
Version used:
as above
Devices/Android versions reproduced on:
All
- Sample project to trigger the issue.
``` xml
// snippet from nav_graph.xml
<fragment
android:id="@+id/bookFragment"
android:name="com.example.bookFragment"
android:label="@string/book_title" >
<argument
android:name="bookId"
app:type="integer" />
</fragment>
// snippet from sttrings.xml
<string name="book_title">book %1$d</string>
```
Some snippets from the way I would otherwise do it.
``` kotlin
// actrivity, implements updateTitle( from an interface (Foo)
override fun updateTitle(title: String) {
toolbar?.title = title
}
// fragment
override fun onResume() {
super.onResume()
// foo is the activity cast to Foo
foo?.updateTitle(getString(R.string.reading_title, 1))
}
```
- A screenrecord or screenshots showing the issue (if UI related).
Actual title vs expected