Nav Deep Link from a foreground service

Viewed 588

I'm trying to navigate to a fragment in my navigation graph from a foreground service notification. I used the below code to create an explicit deep link which didn't work out as expected.

val pendingIntent = NavDeepLinkBuilder(this) // this is the Service context!
            .setComponentName(MainActivity::class.java)
            .setGraph(R.navigation.primary_app_nav)
            .setDestination(R.id.shareSheetFragment)
            .setArguments(bundle)
            .createPendingIntent()

After reading the guide, I got to know that you need to use the activity context and not any other context (in my case it is service context).

Note that if the provided context is not an Activity, the constructor uses PackageManager.getLaunchIntentForPackage() as the default activity to launch, if available.

My question is how do I get hold of the MainActivity context from within my foreground service?

1 Answers

I still haven't figured out a way to do it with explicit deep links, but I settled for a solution which leverages implicit deep link.

First, add a deep link to your fragment in the nav graph XML.

<deepLink android:id="@+id/deepLink"
   app:uri="my-app://com.example.app/hotspot?ssid={ssid}&amp;passkey={passkey}" />

Add the <nav-graph /> tag within your <activity /> in the manifest. This will ensure that all the intent filters are created and merged to your manifest.

<nav-graph android:value="@navigation/primary_app_nav" />

Create your PendingIntent for the notification as follows.

fun getNotificationPendingIntent(ssid: String, passkey: String): PendingIntent {
     val uri = Uri.parse("my-app://com.example.app/hotspot?ssid=$ssid&passkey=$passkey")
     val bundle = Bundle().apply { putParcelable(SHARE_DEEP_LINK_KEY, uri) }
     val intent = Intent(this, SailsActivity::class.java).apply {
          putExtras(bundle)
          // to clear the back stack
          addFlags(FLAG_ACTIVITY_CLEAR_TOP or FLAG_ACTIVITY_SINGLE_TOP
                     or FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK)
     }
     return PendingIntent.getActivity(this, launchShareSheetCode, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}

Finally, you need to handle the deep link intents from your Activity class' onCreate() method.

private fun handleDeepLinkIntent() {
    val deepLink = intent.extras?.getParcelable(SHARE_DEEP_LINK_KEY) as? Uri
    deepLink?.also {
        val intent = Intent(this, SenderService::class.java)
        this.bindService(intent, serviceConn, Context.BIND_ABOVE_CLIENT)
        navigateDeepLink(it)
    }
}

private fun navigateDeepLink(uri: Uri) {
    findNavController(R.id.navHostFragment).apply {
        if (graph.hasDeepLink(uri)) 
            navigate(uri)
        else {
            currentDestination?.getAction(id)?.let {
                navigate(id)
            }
        }
    }
}

Deep link URI arguments are automatically available to you in the destination fragment.

if (arguments != null) {
     val ssid = requireArguments().getString("ssid")
     val passkey = requireArguments().getString("passkey")
}
Related