Deep linking with Yozio Mobile Growth API on Android

Overview

The Deep Link of Yozio SuperLinks covers users who already have your app and click on a Yozio SuperLink. There are a number of ways you can handle the users who already have your app, and the deep link case can be divided into three core pieces of functionality to help you achieve your use case:

  • Opening your app
  • Data passing
  • Tracking

Opening your app on Android

If you’d like to implement more complex user flows for those users who deep link into your app, you must first implement a seamless experience for your users to open your app through a Yozio SuperLink. In order for your app to open when a user clicks on a Yozio SuperLink, your app must first be configured to recognize the SuperLink.

Opening your app via App Chooser

Android uses an operating system level feature called the app chooser to send the user to another app based on an "action" it would like to perform (source). When a user clicks on a Yozio SuperLink, we will redirect the user to a domain completely unique to your app; your app will need to recognize this domain as an intent to open your app. You will do this by adding an intent filter to your manifest file. Here is the intent filter you need to add:

1
2
3
4
5
6
<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:scheme="https" android:host="deeplink.yozio.com" android:pathPrefix="/your-app-key" />
</intent-filter>

Notice that the scheme is https; this scheme is shared by all browsers. The Yozio SuperLink will redirect to https://deeplink.yozio.com/yourappkey and since this scheme is shared by browsers, the app chooser will present the user with options to open with your app as well as with their browser apps. The app chooser enables us to detect with certainty whether a user has the app.

A caveat of this method of app detection is that the user will have the option to use their browser to open the link, instead of your app. To address this, add a second intent filter that will open your custom scheme. For information on implementing a custom scheme for your app, click here. Here is the intent filter you need to add to your app:

1
2
3
4
5
6
<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:scheme="your-app-scheme" android:host="yozio" android:path="/" />
</intent-filter>

The host must be "yozio"; this will allow us to open your app when the user chooses to open the Yozio SuperLink with a browser when presented with the app chooser.

There is another caveat affecting users who choose Chrome when presented with the app chooser. Chrome will not automatically open the custom scheme, and instead redirect to the webpage on https://deeplink.yozio.com/your_app_key. On this web page, we’ve provided a customizable landing page where your users can press a button to open your app.

Once you’ve added your two intent filters, simply go to the redirect settings of your SuperLinks and enable "Deep Link" for Android. Once there, enter your custom scheme.

  • For individual SuperLinks, go to Yozio Console **> SuperLinks** >** Organic Links > Edit (the SuperLink you’d like to modify) > Redirect Settings** >** Android** >** Do you want to override default settings?** >** Yes > *Deep Link User? *> Yes* > **then enter your custom scheme

  • For all SuperLinks, go to Yozio Console >** Settings > Default Redirect Settings > Android > Deep Link User? > Yes* > **then enter your custom scheme.

Make sure to enter your custom scheme in the *Android Custom Scheme** field.

We recommend implementing Yozio’s support for Android App Links to ensure your Android 6+ users have the best experience.

With Android 6, Google has released App Links, which allow a more seamless transition between apps that bypasses the app chooser, but still guarantees that the correct app is opened.

Check out this article for information on how to integrate Yozio’s support for App Links for your users on Android 6 and above. Additionally, check out the section "Data Passing Over Deep Links" for information on passing metadata into your app.

Once inside the app, you’ll want your app to parse the data from the corresponding click event. Since the click event results in the app opening directly, all data appended to the SuperLink URL is preserved and passed into the app; all that is required is that the metadata is parsed into something that the app can easily access. Additionally, this is where the app opening is confirmed to have come from a click on a Yozio SuperLink; clicks on Yozio SuperLinks carry a piece of metadata indicating that the app was opened through a SuperLink, and not through other means.

On Android, retrieving metadata is as easy as parsing the URL from the intent. When other apps (such as Chrome, Gmail, or SMS) open your app, they pass the URL that opened the app to the app itself over the intent.

1
2
String referrer = intent.getStringExtra("referrer");
Uri uri = intent.getData();

Call getStringExtra("referrer"); in the Broadcast receiver; this call will retrieve the referrer string from the Google Play new install broadcast

Call intent.getData(); in the Launcher or Main Activity (whichever Activity is launched first when a user opens the app). This will get the URL that led to the app opening. Once you have this URL, parse the query string out of the URL. Here is some sample code demonstrating this:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
protected void onCreate(Bundle savedInstanceState) {
    Intent intent = this.getIntent();
    HashMap<String, Object> metaData;

    if (uri.getEncodedQuery() == null) { 
        metaData = getMetaDataFromCustomUrlScheme(uri);
    } else {
        String metaDataString = uri.getQueryParameter("__ymd");
        metaData = parseQueryStringToHashMap(metaDataString);
    }
}

private HashMap<String, Object> parseQueryStringToHashMap(String queryString) {

    HashMap<String, Object> params = new HashMap<String, Object>();

    if (queryString == null) {
        YozioService.log(Constants.LOG_LEVEL.INFO, "parseQueryStringToHashMap - queryString is null.");
        return params;
    }

    YozioService.log(Constants.LOG_LEVEL.INFO, "parseQueryStringToHashMap - queryString is " + queryString);
    queryString = queryString.trim();

    if (queryString.length() == 0) {
        return params;
    }

    String[] queryParamParts = queryString.split("&");

    for (String param : queryParamParts) {
        String pair[] = param.split("=");
        String key, value = "";

        try {
            key = URLDecoder.decode(pair[0], "UTF-8");
        } catch (UnsupportedEncodingException e) {
            key = pair[0];
        }
        if (pair.length > 1) {
            for(int i = 1; i < pair.length; i++) {

                if (i > 1) {
                    value += "=";
                } else {
                    value += pair[i];
                }

                try {
                    value = URLDecoder.decode(pair[1], "UTF-8");
                } catch(UnsupportedEncodingException e) {
                    value = pair[1];
                }
            }
            params.put(key, value);
        }
    }
        YozioService.log(Constants.LOG_LEVEL.INFO, "parseQueryStringToHashMap - keyValuePairs is " + params.toString());
        return params;
}

private HashMap<String, Object> getMetaDataFromCustomUrlScheme(Uri uri) {

    HashMap<String, Object> metaData = new HashMap<String, Object>();
    String[] pairs = deeplinkFragment.split(";");

    for (String pair : pairs) {
        // valid format eg "S.age=25"
        YozioService.log(Constants.LOG_LEVEL.INFO, "getMetaData from Custom Url Scheme - pair is [" + pair + "]");

        if (!pair.startsWith("S.")) {
            continue;
        }
        if (pair.startsWith("S.=")) { // eg S.=25
            continue;
        }
        if (!pair.contains("=")) { // ie "S.age25"
            continue;
        }

        // now we should have the correct format ie "S.age=25"

        pair = pair.replace("S.", "");
        int index = pair.indexOf("=");
        String key = pair.substring(0, index); // age

        try {
            key = URLDecoder.decode(key, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // ignore
        }

        String value = pair.substring(index+1); // 25

        try {
            value = URLDecoder.decode(value, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // ignore
        }

        metaData.put(key, value);
    }

    YozioService.log(Constants.LOG_LEVEL.INFO, "getMetaData from Custom Url Scheme - metaData is " + metaData.toString());
    return metaData;

}