In my app, the user can select a category, then select an item within that category to finally view the item details. The standard/forward flow is:
SelectCategoryFragment
->SelectItemFragment
->ViewItemDetailsFragment
On selecting a category, the selectedCatId
is passed via a Bundle
from SelectCategoryFragment
to SelectItemFragment
:
NavController navController = Navigation.findNavController(v);
Bundle args = new Bundle();
args.putLong(SelectItemFragment.ARG_CATEGORY_ID, selectedCatId);
navController.navigate(R.id.action_nav_categories_to_items, args);
SelectItemFragment
will then use the getArguments().getLong(ARG_CATEGORY_ID)
value to query and display the appropriate items from the selected category.
That works fine. But I am now trying to implement deep linking when the users taps on a Notification, jumping them straight to ViewItemDetailsFragment
with a backstack that can take them up to SelectItemFragment
, then SelectCategoryFragment
.
My problem is that, as described, SelectItemFragment
depends on the ARG_CATEGORY_ID
argument being passed to it in order to retrieve/display its data. I've read up on deep linking and nested navigation graphs, but don't really know how to pass ARG_CATEGORY_ID
with deep linking/backstacks.
Is there a tidy way I can pass data from ViewItemDetailsFragment
to SelectItemFragment
when the user presses back?
Open new activity on click of push notification. Display data coming from push notification of new activity. If the application is closed so after click on notification the app get started.
Deep linking allows push notifications to send users directly to specific pages within an app, making it easy for users to reconsider an abandoned cart, view new content, shop a sale, or fall in love with new features.
From today, media partners will be able to promote their channels on Snapchat via deep links - which means publishers will be able to link to their Snapchat content direct from one app to another, making it easier to promote, share and (at least in some regard) measure the performance of Snapchat content.
Although, I had deep linking figured out, still there was no definitive approach to pass a deep link in push notification and let the link do the magic of taking the user to the intended screen.
Generally, a deep link is a type of link with a custom scheme for taking users to a specific point or content in the app. In our case we want our users to be taken to the details page of our app. For example, since we want to deep link into the details page, we can construct our deep links like so: myapp://user/details/123456
In the Custom Data field, add a link key, with value to one of the valid deep links. Assuming you have your device’s FCM token handy, send a test notification.
Tapping on a search result that contains a deep link will fire an event that can be handled by an application, and is typically used to navigate to the page referenced from the deep link. The sample application demonstrates a Todo list application where the data is stored in a local SQLite database, as shown in the following screenshots:
TLDR: problem might be solved using nested graphs. Refer to this article for more details.
Let's define simple fragment FragmentRed
, FragmentGreen
and FragmentBlue
who inflate simple layouts each with corresponding background color:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_red_dark">
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
And declare fragment classes as such:
class FragmentRed : Fragment() {
private val args: FragmentRedArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.red, container, false)
view.findViewById<TextView>(R.id.textView).text = args.foo.toString()
return view
}
}
FragmentGreen
and FragmentBlue
are copy pasted, but substituted all color texts with corresponding color texts, i.e. FragmentRedArgs
-> FragmentBlueArgs
, R.layout.red
-> R.layout.blue
.
Let's declare main activity layout as such:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:id="@+id/content"
android:layout_height="match_parent">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/main_graph" />
</FrameLayout>
Where main_graph
is:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_graph"
app:startDestination="@id/fragment_red">
<fragment
android:id="@+id/fragment_red"
android:name="com.playground.FragmentRed">
<argument
android:name="foo"
android:defaultValue="0"
app:argType="integer" />
<action
android:id="@+id/action_fragment_red_to_fragment_green"
app:destination="@id/fragment_green" />
</fragment>
<navigation
android:id="@+id/secondLevel"
app:startDestination="@id/fragment_green">
<fragment
android:id="@+id/fragment_green"
android:name="com.playground.FragmentGreen">
<argument
android:name="bar"
android:defaultValue="0"
app:argType="integer" />
<action
android:id="@+id/action_fragment_green_to_fragment_blue"
app:destination="@id/fragment_blue" />
</fragment>
<fragment
android:id="@+id/fragment_blue"
android:name="com.playground.FragmentBlue">
<argument
android:name="zar"
android:defaultValue="0"
app:argType="integer" />
</fragment>
</navigation>
</navigation>
Now inside MainActivity
let's spawn a new notification, passing in arguments for each of fragments: "foo" (red) - 1, "bar" (green) - 2, "zar" (blue) - 3.
Our expectation is upon clicking on notification to open Blue screen with text 3, upon back click see Green screen with 2 and another click should bring Red screen with 1 on the screen:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = findNavController(R.id.nav_host_fragment)
val pendingIntent = navController.createDeepLink()
.setGraph(R.navigation.main_graph)
.setDestination(R.id.fragment_blue)
.setArguments(bundleOf("foo" to 1, "bar" to 2, "zar" to 3))
.createPendingIntent()
createNotificationChannel() // outside of the scope of this answer
val builder = NotificationCompat.Builder(this, "my_channel")
.setContentTitle("title")
.setContentText("content text")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.android)
.setAutoCancel(true)
.setChannelId("channelId")
with(NotificationManagerCompat.from(this)) {
notify(100, builder.build())
}
}
}
Here's the actual behavior on device:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With