Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deep linking from Notification - how to pass data back up through the backstack?

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?

like image 201
ban-geoengineering Avatar asked Jun 15 '20 17:06

ban-geoengineering


People also ask

How do I get notification data on Android?

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.

What is deep link in push notifications?

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.

What does Added by deep link mean on Snapchat?

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.

Is it possible to pass a deep link in push notifications?

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.

What is a deep link in an app?

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

How do I test deep links with custom data?

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.

What happens when you tap on a deep link?

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:


Video Answer


1 Answers

TLDR: problem might be solved using nested graphs. Refer to this article for more details.

Longer answer

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:

enter image description here

like image 110
azizbekian Avatar answered Oct 16 '22 08:10

azizbekian