Issue
When a user enters a geo-fence in our app, we show them an offer notification about the area, which when clicked, should direct them to a specific composable screen called SingleNotification
. I've followed google's codelab and their documentation but I haven't managed to make the navigation to the specific screen work yet. Right now, clicking on the notification or running the adb shell am start -d “eway://station_offers/date_str/www.test.com/TITLE/CONTENT” -a android.intent.action.VIEW
command, simply opens the app.
The activity is declared as follows in the manifest:
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<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:host="station_offers"
android:scheme="eway" />
</intent-filter>
</activity>
Our MainNavController class contains the NavHost which in turn contains various NavGraphs. I've only included the relevant graph below:
NavHost(
navController = navController,
startDestination = NavigationGraphs.SPLASH_SCREEN.route
) {
....
notificationsNavigation()
....
}
The notificationsNavigation graph is defined as follows:
fun NavGraphBuilder.notificationsNavigation() {
navigation(
startDestination = Screens.NOTIFICATION_DETAILS.navRoute,
route = NavigationGraphs.NOTIFICATIONS.route
) {
composable(
route = "${Screens.NOTIFICATION_DETAILS.navRoute}/{date}/{imageUrl}/{title}/{content}",
arguments = listOf(
navArgument("date") { type = NavType.StringType },
navArgument("imageUrl") { type = NavType.StringType },
navArgument("title") { type = NavType.StringType },
navArgument("content") { type = NavType.StringType }
),
deepLinks = listOf(navDeepLink {
uriPattern = "eway://${Screens.NOTIFICATION_DETAILS.navRoute}/{date}/{imageUrl}/{title}/{content}"
})
) { backstackEntry ->
val args = backstackEntry.arguments
SingleNotification(
date = args?.getString("date")!!,
imageUrl = args.getString("imageUrl")!!,
title = args.getString("title")!!,
description = args.getString("content")!!
)
}
}
}
The Screes.NOTIFICATION_DETAILS.navRoute
corresponds to the value of notification_details
.
Inside the geo-fence broadcast receiver, I construct the pending Intent as follows:
val deepLinkIntent = Intent(
Intent.ACTION_VIEW,
"eway://station_offers/${
offer.date
}/${
offer.image
}/${offer.title}/${offer.content}".toUri(),
context,
MainActivity::class.java
)
val deepLinkPendingIntent: PendingIntent =
TaskStackBuilder.create(context!!).run {
addNextIntentWithParentStack(deepLinkIntent)
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)!!
}
showNotification(offer.title, offer.content, deepLinkPendingIntent)
I can't figure out what I'm missing here.
Solution
It turns out that the limitations described in this answer are not entirely true. Specifically,
- It is possible to deep link from a notification directly into a destination that is inside a nested graph
- There is no relation between a destination's route and the deepLink URI.
Point 2 above was the key to unlock my understanding of how deeplinks work. They are just arbitrary URIs and have no relationship to the destination's route at all. The rule is that the following 3 items must match up
- The URI pattern defined in a composable's
navDeepLink
DSL - The URI used to construct a
PendingIntent
for the notification - The
scheme
andhost
declared in theintent-filter
in the manifest.
Here are some code snippets. In my case the URIs were static, so you will need to make adjustments in order to address the OP's situation. This example has the following structure
LandingScreen
("landing_screen_route"
)SecondScreen
("second_screen_route"
)- A nested graph (
"nested_graph_route"
) with aNestedScreen
("nested_destination_route"
)
We are going to see how to reach both SecondScreen
and NestedScreen
from a notification.
First, defining the NavGraph using the DSL. Pay special attention to the navDeepLink
entries here.
@Composable
fun AppGraph(onNotifyClick: () -> Unit) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "landing_screen_route"
) {
composable("landing_screen_route") {
LandingScreen {
navController.navigate("second_screen_route")
}
}
composable(
route = "second_screen_route",
deepLinks = listOf(
navDeepLink { uriPattern = "myapp://arbitrary_top_level" } // Note that this pattern has no relation to the route itself
)
) {
SecondScreen {
navController.navigate("nested_graph_route")
}
}
navigation(
startDestination = "nested_destination_route",
route = "nested_graph_route"
) {
composable(
route = "nested_destination_route",
deepLinks = listOf(
navDeepLink { uriPattern = "myapp://arbitrary_nested" } // Note that this pattern has no relation to the route itself
)
) {
NestedScreen(onNotifyClick)
}
}
}
}
Next, here's how you would construct the PendingIntent for both these cases:
val notNestedIntent = TaskStackBuilder.create(this).run {
addNextIntentWithParentStack(
Intent(
Intent.ACTION_VIEW,
"myapp://arbitrary_top_level".toUri() // <-- Notice this
)
)
getPendingIntent(1234, PendingIntent.FLAG_UPDATE_CURRENT)
}
val nestedIntent = TaskStackBuilder.create(this).run {
addNextIntentWithParentStack(
Intent(
Intent.ACTION_VIEW,
"myapp://arbitrary_nested".toUri() // <-- Notice this
)
)
getPendingIntent(2345, PendingIntent.FLAG_UPDATE_CURRENT)
}
Finally, here are the intent-filter
entries in the manifest
<activity
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<!--
The scheme and host must match both of the below:
1. The navDeepLink declaration
2. The URI defined in the PendingIntent
-->
<data
android:scheme="myapp"
android:host="arbitrary_top_level"
/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<!--
The scheme and host must match both of the below:
1. The navDeepLink declaration
2. The URI defined in the PendingIntent
-->
<data
android:scheme="myapp"
android:host="arbitrary_nested"
/>
</intent-filter>
</activity>
Answered By - curioustechizen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.