Skip to content

Deep Link

Deep linking is the use of an URL that takes users directly to specific content in your app and optionally provides data to that content. The ability can be broken down into a few specific actions:

  • Define the URI link and its data
  • Create an intent filter in the manifest
  • Receive the intent, parse the data, and load the configuration

The URI link contains the android application package name, the attributes specified in the intent filter, and the configuration data used by the referenced configuration provider.

The following HTML encoded generic link and intent filter shows a templated example of a deep link and the format of the data used by a Configuration Loader provider:

1
<a href="android-app://<Android_Application_PackageName>/<Scheme>/<Host>/<Path>?data={&quot;<ConfigurationProviderID>&quot;:[{&quot;<ProviderSpecific_DataKey>&quot;:&quot;<ProviderSpecific_DataKey_Value>&quot;}]}"> Launch DeepLink application</a>
1
2
3
4
5
<intent-filter>
   <data android:scheme="https" android:host="<Host>"/>
   <data android:scheme="<Scheme>" android:host="<Path>" android:pathPrefix="/"/>
...
</intent-filter>

The following HTML encoded link and intent filter shows a real example of a deep link and the data used by the Discovery Service Configuration Provider:

1
<a href="android-app://com.myApp.package/myApp/www.example.com/configuration?data={&quot;com.sap.configuration.provider.discoveryservice&quot;:[{&quot;email_address&quot;:&quot;user@ad9a3f3b6.pilot.sapmobileplace.com&quot;}]}"> Launch DeepLink application</a>
1
2
3
4
5
<intent-filter>
   <data android:scheme="https" android:host="www.example.com" />
   <data android:scheme="myApp" android:host="configuration" android:pathPrefix="/"/>
...
</intent-filter>

The above examples, show how you should specify the attributes contained within the intent filter in the manifest file. The elements in the deep link URI and the intent filter must match.

Create an Intent Filter in the Manifest

To enable the link handling for the app content, you must include an intent filter in your manifest file that contains the following elements and attribute values:

  • <action> – Include the ACTION_VIEW intent action so that the intent filter can be reached from Google Search.

  • <category> – Include the BROWSABLE category. It is required in order for the intent filter to be accessible from a web browser. Also, include the DEFAULT category. This allows the app to respond to implicit intents.

  • <data> – Include the <data> tags, each of which represents a URI format that resolves to an activity. Each <data> tag includes the following attributes: android:scheme, android:host, and android:pathPrefix. This was previously covered in the section above since its data must correspond with the values defined by the deep link URI.

The following XML snippet shows how you might specify an intent filter in your manifest for deep linking:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<activity
    android:name=".DeepLinkActivity">
    <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="www.example.com" />
        <data android:scheme="myApp" android:host="configuration" android:pathPrefix="/"/>
    </intent-filter>
</activity>

Receive the Intent, Parse the URI Data, and Load the Configuration

In the following example, the deep link data is extracted from the intent. The data must be uri-decoded to translate any escape sequences into their corresponding special characters. The deep link data is then parsed into a UserInput object using the static method parseJson of the UserInputs class. This UserInput object is then passed to the ConfigurationLoader constructor. Please refer to Creating a ConfigurationLoader for more details on constructing a configuration loader.

Finally, the loadConfiguration method is called to produce the configuration data. It accomplishes this by passing the input data to the associated provider enabling it to retrieve and load the configuration accordingly.

The second parameter of the parseJson method is a list of supported provider identifiers. If one or more custom providers is created then the ProviderIdentifier class is extended and a new list containing the custom provider identifiers must be passed to parseJson.

 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
public class DeepLinkActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Extract the deep link data from the intent
        String deepLinkData;
        Intent thisIntent = getIntent();
        Uri intentUri = thisIntent.getData();
        if (intentUri != null) {
            try {
                String data = URLDecoder.decode(intentUri.toString(), "UTF-8");
                deepLinkData = data.split("data=")[1];
            } catch (Exception e) {}
        }

        // Parse the deep link data into a User Inputs object - Note: JSON Provider input is ignored
        UserInputs initialUserInputs = UserInputs.parseJson(deepLinkData, ProviderIdentifier.KNOWN_PROVIDERS);

        // Instantiate the Configuration Loader with initial inputs extracted from the deep link
        ConfigurationLoader configurationLoader = new ConfigurationLoader(
            getApplicationContext(),
            configurationLoaderCallback,
            initialUserInputs
        );
        configurationLoader.loadConfiguration();
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class DeepLinkActivity : AppCompatActivity() {
    override fun onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState)

        // Extract the deep link data from the intent
        val deeplinkData: String? = when(intent.data != null) {
            true -> {
                val data = URLDecoder.decode(this.toString(), "UTF-8")
                data.split("data=".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()[1]
            }
            false -> null
        }

        // Parse the deep link data into a User Inputs object - Note: JSON Provider input is ignored
        val initialUserInputs = UserInputs.parseJson(deepLinkData, ProviderIdentifier.KNOWN_PROVIDERS)

        // Instantiate the Configuration Loader with initial inputs extracted from the deep link
        val configurationLoader = ConfigurationLoader(applicationContext, configurationLoaderCallback, initialUserInputs).apply {
            loadConfiguration()
        }
    }
}

Providing deep link data to the JSON provider is a security risk, and is therefore excluded by default. If it is desired to supply JSON input in a case where the risk has been mitigated, then this default can be overridden simply by passing an empty exclusion list to parseJson. This is shown in the following example:

1
2
3
4
5
6
// Parse the deep link data into a User Inputs object - Note: JSON Provider input is allowed
UserInputs initialUserInputs = UserInputs.parseJson(
    deepLinkData,
    ProviderIdentifier.KNOWN_PROVIDERS,
    new ProviderIdentifier[] {}
);
1
2
3
4
5
val initialUserInputs = UserInputs.parseJson(
    deepLinkData,
    ProviderIdentifier.KNOWN_PROVIDERS,
    arrayOf<ProviderIdentifier>()
)