How to integrate Firebase Dynamic Links in Android

Noman SadiqNoman Sadiq
8 min read

Initial Steps

  1. If you haven't done so already, create a new project in the Firebase console.

  2. Click on "Add Firebase to your Android app" and follow the setup steps.

  3. When prompted, enter your app's package name, for example, com.android.myapp

  4. At the end, you'll download a google-services.json file. You can download this file again at any time.

  5. If you haven't done so already, copy this into your project's module folder, typically app/.

  6. In the Firebase console, enable Dynamic Links for your project.

  7. Once Dynamic Links are enabled, you can create new Dynamic Links.

Add the dependency for Dynamic Links to your app/build.gradle file:

dependencies {
    // ...
    // For Java (Works for Kotlin as well)
    implementation 'com.google.firebase:firebase-dynamic-links:20.1.1'
    // Specifically For Kotlin
    implementation 'com.google.firebase:firebase-dynamic-links-ktx:20.1.1'
}

Then sync your project.

Setting up the manifest

        <activity
            android:name="com.android.myapp.SplashActivity"
            android:exported="true"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="https" android:host="mydomain.page.link"/>
                <data android:scheme="http" android:host="mydomain.page.link"/>
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>

SplashActivity is the activity that handles the links. This XML code has an intent-filter that's used in an Android app's manifest file. It tells the Android system that your app wants to respond to a certain type of intent. Here's what each part does:

  • <intent-filter android:autoVerify="true">: Starts the intent filter. The android:autoVerify="true" attribute tells the Android system to verify whether the links being opened belong to your app or not.

  • <action android:name="android.intent.action.VIEW" />: This line says that your app wants to respond to VIEW intents. A VIEW intent is typically used to ask an app to display some data, such as a web page.

  • <category android:name="android.intent.category.DEFAULT" />: This line is required for your app to receive implicit intents (intents that don’t specify a specific app).

  • <category android:name="android.intent.category.BROWSABLE" />: This line is required for the link to be accessible from a web browser without the user having to explicitly open it in your app.

  • The <data> elements specify the type of data your app wants to respond to. In this case, it’s saying that your app wants to respond when these specific URLs are opened:

    • https://mydomain.page.link

    • http://mydomain.page.link

So, if any of these URLs are opened, and if your app is installed on the device, then your app will be considered as a candidate to handle the intent. But they are by default disabled under app settings' supported web addresses. We can make it enabled. Here's how

Enable supported web addresses by default

Firebase automatically uploads the assetlinks.json. But you must add the SHA256 fingerprint for the Android app. For release, you will need to add Play Store SHA256 signing keys. You can find the Play Store SHA256 signing keys under the Play Console developer account under Release > Setup > App Integrity. and then select your app as the handler for the dynamic link in the Firebase dynamic links console.

Firebase will generate mydomain.page.link/.well-known/assetlinks.json

It will take up to 2 hours for this file to be updated.

The content of this file will be like below

[
    {
        "relation": [
            "delegate_permission/common.handle_all_urls"
        ],
        "target": {
            "namespace": "android_app",
            "package_name": "com.android.myapp",
            "sha256_cert_fingerprints": [
                "8D:9:E0:87:A7:DA:A0:C4:14:F5:F2:C2:E3:08:C3:EC:96:F8:B8:6A:18:67:B2:9A:27:95:93:C0:72:A7:C1:87"
            ]
        }
    }
]

After that, the switch to toggle the supported web address http://mydomain.page.link on or off will disappear. This indicates that the address is permanently enabled.

Override the onNewIntent() method in your main activity. This method gets called when an intent is set on an activity that is already running in the current task.

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
}

In Kotlin

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        setIntent(intent)
    }

The onNewIntent() method is part of the Activity lifecycle in Android. It’s called when an instance of an activity is brought to the foreground (made visible to the user). This can happen either because a new instance of an activity is being created, or because an existing one is being brought to the foreground.

When you override the onNewIntent() method in your main activity, you’re telling your app what to do when it receives a new intent while it’s already running. In the context of Firebase Dynamic Links, this is where you would handle the incoming deep link.

  • setIntent(intent); This line updates the original intent that started the activity with the new intent. This is useful in cases where you want to update the data being used by the activity.

By doing this, your app can handle dynamic links even when it’s already running and in the foreground. When a user clicks on a dynamic link, this method will be triggered, allowing your app to react accordingly.

You can create dynamic links in the Firebase console or programmatically. You can use these helper classes

In Java

package com.android.myapp;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.ProgressBar;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
import com.google.firebase.dynamiclinks.ShortDynamicLink;

public class Links {
    public static final String DOMAIN_URI_PREFIX = "https://mydomain.page.link";

    public static class ListLink extends Links {
        int listing;
        int userId;
        String title;

        public ListLink(int listing, int userId, String title) {
            this.listing = listing;
            this.userId = userId;
            this.title = title;
        }

        @Override
        public String toLink() {
            return "https://www.mydomain.com/?listing=" + listing + "&userId=" + userId + "&title=" + title;
        }
    }

    public static class ProfileLink extends Links {
        int user;

        public ProfileLink(int user) {
            this.user = user;
        }

        @Override
        public String toLink() {
            return "https://www.mydomain.com/?user=" + user;
        }
    }

    public static class PostLink extends Links {
        int postId;

        public PostLink(int postId) {
            this.postId = postId;
        }

        @Override
        public String toLink() {
            return "https://www.mydomain.com/?postId=" + postId;
        }
    }

    public String toLink() {
        return "";
    }

    public static void createShortLinkAndShare(final Context context, Links data, final ProgressBar progressBar) {
        if (progressBar != null) progressBar.setVisibility(ProgressBar.VISIBLE);
        String baseUrl = data.toLink();
        FirebaseDynamicLinks.getInstance().createDynamicLink()
                .setLink(Uri.parse(baseUrl))
                .setDomainUriPrefix(Links.DOMAIN_URI_PREFIX)
                .buildShortDynamicLink()
                .addOnSuccessListener(new OnSuccessListener<ShortDynamicLink>() {
                    @Override
                    public void onSuccess(ShortDynamicLink shortDynamicLink) {
                        if (progressBar != null) progressBar.setVisibility(ProgressBar.GONE);
                        Uri shortLink = shortDynamicLink.getShortLink();
                        if (shortLink != null)
                            shareLink(context, shortLink);
                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        if (progressBar != null) progressBar.setVisibility(ProgressBar.GONE);
                        // Show dialog with error message
                    }
                });
    }

    private static void shareLink(Context context, Uri link) {
        Intent shareIntent = new Intent();
        shareIntent.setAction(Intent.ACTION_SEND);
        shareIntent.putExtra(Intent.EXTRA_TEXT, link.toString());
        shareIntent.setType("text/plain");
        context.startActivity(Intent.createChooser(shareIntent, "Share link using"));
    }

    public static void createShortLinkAndShare(Fragment fragment, Links data, ProgressBar progressBar) {
        createShortLinkAndShare(fragment.requireActivity(), data, progressBar);
    }
}

Usage

createShortLinkAndShare(this, new Links.ListLink(123, 456, "My Title"), progressBar);

In Kotlin

First, add the dependency

implementation "org.jetbrains.kotlin:kotlin-reflect:1.8.21"
package com.android.myapp

// These are the imports for your Android app
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.widget.ProgressBar
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks
import com.android.myapp.dialog // This is a custom dialog function from your app
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties

interface Links // This is an interface for your link data classes

const val DOMAIN_URI_PREFIX = "https://mydomain.page.link" // This is the prefix for your Firebase Dynamic Links

// These are data classes that implement the Links interface and represent different types of links in your app
data class ListLink(val listing: Int, val userId: Int, val title: String) : Links
data class ProfileLink(val user: Int) : Links
data class PostLink (val postId: Int): Links

// This function converts a Links object to a URL string
fun Links.toLink(): String {
    val params = this::class.memberProperties.mapNotNull { prop ->
        (prop as? KProperty1<Links, *>)?.let { "${it.name}=${it.get(this)}" }
    }.joinToString("&")
    return "https://www.mydomain.com/?$params"
}

// This function creates a short Dynamic Link and shares it using an Android share intent
fun Context.createShortLinkAndShare(data: Links, progressBar: ProgressBar? = null) {
    progressBar?.isVisible = true // Show the progress bar while the link is being created
    val baseUrl = data.toLink() // Convert the data to a URL string
    FirebaseDynamicLinks.getInstance().createDynamicLink() // Start creating a Dynamic Link
        .setLink(Uri.parse(baseUrl)) // Set the link parameter to the URL string
        .setDomainUriPrefix(DOMAIN_URI_PREFIX) // Set the domain URI prefix to your Firebase Dynamic Links domain
        .buildShortDynamicLink() // Build a short Dynamic Link
        .addOnSuccessListener { result -> // Handle the success case
            progressBar?.isVisible = false // Hide the progress bar when done
            val shortLink = result.shortLink // Get the short Dynamic Link from the result
            if (shortLink != null)
                shareLink(shortLink) // Share the link if it's not null
        }
        .addOnFailureListener { exception -> // Handle the failure case
            progressBar?.isVisible = false // Hide the progress bar when done
            dialog { exception.localizedMessage } // Show a dialog with the error message
        }
}

// This function shares a link using an Android share intent
private fun Context.shareLink(link: Uri) {
    val shareIntent = Intent().apply {
        action = Intent.ACTION_SEND // Set the action to ACTION_SEND to send data to other apps
        putExtra(Intent.EXTRA_TEXT, link.toString()) // Put the link in the intent as extra text data
        type = "text/plain" // Set the type of data being sent (plain text)
    }
    startActivity(Intent.createChooser(shareIntent, "Share link using")) // Start a chooser activity to let the user pick an app to share with
}

// This function is a convenience function for fragments to create and share a short Dynamic Link 
fun Fragment.createShortLinkAndShare(data: Links, progressBar: ProgressBar? = null) =
    requireActivity().createShortLinkAndShare(data, progressBar)

Usage

In your fragment or activity

createShortLinkAndShare(PostLink(pid), progressbar)

In Java

// If the app is opened by a firebase link
FirebaseDynamicLinks.getInstance()
    .getDynamicLink(getIntent())
    .addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() {
        @Override
        public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
            // Get deep link from result (may be null if no link is found)
            Uri deepLink = null;
            if (pendingDynamicLinkData != null) {
                deepLink = pendingDynamicLinkData.getLink();
            }

        if (deepLink != null) {
            // Extract the query parameters from the deep link
            // These parameters are defined in the Links data classes
            // Assuming that these variables are already declared.
            userId = deepLink.getQueryParameter("user");
            listingId = Integer.parseInt(deepLink.getQueryParameter("listing"));
            listTitle = deepLink.getQueryParameter("title");
            listUserId = Integer.parseInt(deepLink.getQueryParameter("userId"));
            postId = Integer.parseInt(deepLink.getQueryParameter("postId"));
        }
    }
})
.addOnFailureListener(this, new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        e.printStackTrace();
        // Handle the error case here
    }
});

In Kotlin

        //If the app is opened by a firebase link
        Firebase.dynamicLinks
            .getDynamicLink(intent)
            .addOnSuccessListener(this) { pendingDynamicLinkData: PendingDynamicLinkData? ->
                // Get deep link from result (may be null if no link is found)
                var deepLink: Uri? = null
                if (pendingDynamicLinkData != null) {
                    deepLink = pendingDynamicLinkData.link
                }

                deepLink?.let {
                    // Extract the query parameters from the deep link
                    // These parameters are defined in the Links data classes
                    // Assuming that these variables are already declared.
                    userId = it.getQueryParameter("user") ?: ""
                    listingId = it.getQueryParameter("listing")?.toInt() ?: 0
                    listTitle = it.getQueryParameter("title") ?: ""
                    listUserId = it.getQueryParameter("userId")?.toInt() ?: 0
                    postId = it.getQueryParameter("postId")?.toInt() ?: 0
                }
            }
            .addOnFailureListener(this) { e -> e.printStackTrace() }
0
Subscribe to my newsletter

Read articles from Noman Sadiq directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Noman Sadiq
Noman Sadiq

I am an Android Application Developer. If you have any queries reach me out at my email nomanokr@gmail.com