Sitemap
Metakratos Studio

Digital Power for Startups and Enterprises to Create Products of the Future

Deep Dive Into Deep Link. Part 4. Handling links issues

9 min readOct 10, 2023

--

Problems: links without a scheme, second host, intercepting anything

Press enter or click to view image in full size

Introduction

Hi everyone! Today we are going to tell you about Deep Link again. Last time we found out this:

Hypothesis 3. Different OS shells — BUSTED

The Android OS shell does not affect Deep Link’s performance.

Hypothesis 4. Applications — CONFIRMED

The application, from which the link is opened, does affect the work of Deep Link.

As usual, it is not without subtleties: the shell can influence processing, but through its own (shell-preinstalled) applications. The applications have built-in browsers, but even they did not affect the clear conclusion — applications affect deep link operation.

Today we’re wrapping up the breakdown of our seven problems. Finally, get ready, we’re about to start!

Problem №5. Links without scheme

Links like host/path were handled differently

Same link, same device, but different applications (Messaging, Gmail):

Press enter or click to view image in full size
X-1 Configuration Table
Press enter or click to view image in full size
Y-1 Configuration Table

Now the experiments:

  • X-1 (Messaging). If we click on the link, we will be prompted to go to our application
  • Y-1 (Gmail). Clicking on the link DOES NOT invite us to go to our app

We already know that the application affects deep link processing. But we have not yet tried to follow links without scheme. As you can see above, following a link is possible even though there is no scheme. Assume:

Hypothesis 5

There is a special mechanism for processing links without a scheme

You may get a feeling that if you specify a null scheme in the manifest, links without a scheme will work. Let’s try:

Press enter or click to view image in full size
null scheme for intent-filter

There is another way to test deep link, besides manually clicking on links. To do this, you need to call the adb command:

adb shell am start -W -d null:developer.android.com 

This command will start the intent with the specified Data URI (-d) and wait for it to complete (-W).

If you configure an appropriate intent-filter, the OS will be able to understand if your application can handle this intent. This is what we have done. If we run application and try to run adb command shown above we won’t get to our application. This means that

android:scheme=”null” is a specific value of scheme, not its absence.

If null is not the absence of scheme, then maybe we should not specify it at all? Interesting suggestion. Let’s configure the intent-filter:

Press enter or click to view image in full size
Manifest with configured intent-filter without android:scheme attribute

The studio suggests that we have not specified the mandatory attribute scheme. But in spite of that we will be able to compile.

Let’s try to run intent through adb, but this time without scheme:

adb shell am start -W -d developer.android.com 

In the console, we can see:

Starting: Intent { dat=developer.android.com/about }
Error: Activity not started, unable to resolve Intent { dat=developer.android.com/about flg=0x10000000 }

Intent resolution failed, even though the intent-filter was configured. If you click on the link you will be redirected to the browser, not our applicaiton.

Let’s turn to the documentation. It says that at least one scheme attribute must be set for the filter, otherwise no other attribute will be processed (we saw a similar message in Studio):

“…at least one scheme attribute must be set for the filter, or none of the other URI attributes are meaningful.”

- What if we write android:scheme=””?

- It will be the same as for the example just described.

After several attempts, we have an assumption that the processing is affected by http. This turned out to be indeed the case. When opening the link developer.android.android.com/about in browser http scheme is automatically substituted to the URL.

Let’s look at something more convincing. Open logcat:

I/ActivityTaskManager: START u0 {
act=android.intent.action.VIEW
dat=http://developer.android.com/about
flg=0x14002000 cmp=com.android.chrome/
org.chromium.chrome.browser.ChromeTabbedActivity
(has extras)
} from uid 10116

There are no questions left here. http scheme was substituted into the URL without scheme. And it was done in application that hold link, not us or anything else! This once again proves how much the application from which the link is opened influences the work of deep link. But how did it happen? Let’s find out further.

Scheme processing by the application

It seems that developer.android.com/about somehow managed to turn into a link by itself. Is it really so? It’s easy to check: let’s add TextView to the screen and specify three kinds of links as values of android:text attribute:

TextView with android:text does not convert URL to clickable link

No magic happened. The links did not become clickable. All because no special handler was configured.

Android has many ways to handle URLs (and scheme, in particular):

Press enter or click to view image in full size
Ways of handling scheme and URLs

Let’s look at each in more detail with an example.

Granted:

Press enter or click to view image in full size
TextView for displaying text
Press enter or click to view image in full size
LiveData, which sets the text in the TextView
Press enter or click to view image in full size
ViewModel, which sets the text in LiveData

android:autoLink

Let’s start with the most concise method. The android:autoLink attribute triggers a pattern search on the contents of the TextView. Among the patterns to search for are email, phone, map. The web constant, runs a search for URL’s. Exactly what is needed there.

Press enter or click to view image in full size
autoLink description. The default setting is none, which disables clickable links from appearing (so nothing happened in the android:text example)

Add autoLink with value web to TextView:

Press enter or click to view image in full size
Using autoLink

Let’s run it. We see that the link has become clickable. Moreover, the click launches the intent, where the URL with automatically inserted http scheme is in the data. Great!

android.text.util.Linkify.addLinks

This static function applies a regular expression to the contents of a TextView and searches for matches. It is similar to the way autoLink works. As an argument to addLinks, you can pass defaultScheme, which will be appended to the link if it does not start with the schema specified in the other parameter.

Get Valery Petrov’s stories in your inbox

Join Medium for free to get updates from this writer.

Call addLinks and pass our TextView, Pattern for web URL, default https scheme, default UrlMatchFilter, and null TransformFilter.

Press enter or click to view image in full size
Example of using the Linkify.addLinks function

Run. The result is the same as in the previous example, but only this time the https scheme was substituted.

android.text.util.Linkify.TransformFilter

Same addLinks function, but this time the transformFilter parameter is used to change the links found.

This time defaultScheme is null and the transformFilter is our HttpsTransformFilter, which adds a prefix to the URL.

Press enter or click to view image in full size
Example of using the Linkify.addLinks function with its own transformFilter

Let’s run it. The result is the same as in the previous example.

android.text.Html

If your application has HTML tags, this method may come in handy. Let’s use the static function fromHtml and the LinkMovementMethod class.

The fromHtml returns the displayed text from html tag and sets a link to it from the href attribute. LinkMovementMethod will present the text passed to it as a clickable link.

Press enter or click to view image in full size
Example of using Html.fromHtml function and LinkMovementMethod. At the moment it is recommended to use a non-deprecated analogue with the flags
Press enter or click to view image in full size
Example of using html tags in strings

Running. The link without scheme has turned into a clickable link and points to an https link.

android.text.style.URLSpan

Now let’s look at the mechanism that underlies most of the previous mechanisms — spannable.

The Spanned interface allows you to add markup to text. One type of Span is URLSpan, which converts selected text into clickable text. Clicking on a URLSpan launches an Intent with ACTION_VIEW. We set up a filter on this action in our application.

In the ViewModel we created a SpannableString, passed the reference without a schema, added the URLSpan, and specified the required reference. Since SpannableString is not an inheritor of String, we change the data type to CharSequence. Pay attention to LinkMovementMethod. It triggers the start of the intent using the link from URLSpan.

Press enter or click to view image in full size
URLSpan Usage Example.
Example of using URLSpan

Let’s get it up and running. It works as it should!

We have broken down the three most common methods of link handling. There are others (for example, Better-Link-Movement-Method), but they usually use the APIs we’ve broken down.

Still remember what hypothesis we were talking about? :) We’ve done a lot of work and we can safely conclude:

Hypothesis 5 — CONFIRMED

There is a special mechanism for processing links without scheme

Problem №6. Second host

We forgot that we have a second domain.

Our project has two hosts: one of them is the main one and the other redirects to the main one. We don’t know in advance which of them a user will use to go to our application. At the same time, both variants must be working.

At first we thought that we could write a different intent filter for each host:

Press enter or click to view image in full size
Separate intent-filter for each host

Then we remembered that all data-tags from one filter will be merged and all sorts of combinations of their attributes will be created. You should not forget about this feature, because it can lead to unexpected consequences :)

We decided not to support two separate intent-filters and rewrote the previous code. It turned out to be much shorter and simpler.

Press enter or click to view image in full size
Two hosts in one intent-filter

Problem №7. Interception of all links

The application started capturing all links with our domain

(even those for which we didn’t have scripts)

There are two resources: privacy-policy and terms-of-use. It is assumed that the user will access them through the browser (we have not explicitly configured deep linking on these links). There are no screens to display this content in our application. But when clicking on the links, the system offers to open our application. We don’t have this content, so the user sees 404. So the question is: “Why did this happen?”.

Originally we wanted to add diplinks to links without path. That is, links that only have a path and a host (e.g. http://developer.android.com). To do this, we configured our intent-filter as follows:

Press enter or click to view image in full size
Intent-filter without android:path* attributes

After that, links without path started to be processed by our application. This is the behaviour we wanted to get. Links to existing content were also processed by our application. Everything is as it should be.

But in addition to that, processing of links with path to non-existing content appeared, which led to 404 errors. The point is that

The absence of an explicitly specified path implies the use of any value.

To get rid of this undesirable behaviour, we had to refuse processing links without path. Otherwise, we would have to handle all kinds of path variations. At that moment we were not ready for it and there was no need for it.

Results

Whew! A lot of new stuff this time:

Hypothesis 5 — CONFIRMED

There is a special mechanism for processing links without scheme.

  • To test deep link it is sometimes convenient to use the following command: adb shell am start -W -d “Data URI”.
  • android:scheme=”null” is a specific value of scheme, not its absence.
  • android:scheme=”” or the absence of this attribute will cause that no other attribute will be processed and deep link will not work.
  • android:autoLink is a short and simple way to make a URL a clickable link.
  • Linkify.addLinks is a function that applies a regular expression to TextView content and searches for matches and has parameters to process.
  • android.text.Html.fromHtml converts html tags to Spanned, URLSpan in particular.
  • android.text.style.URLSpan special span view that converts text to clickable and triggers intent with ACTION_VIEW and Data URL on click
  • android.text.method.LinkMovementMethod handles clicking on a text-link.
  • Data tags are merged with each other. The result is all sorts of combinations of values. For example: http, https, host1, host2 are converted to http://host1, http://host2, https://host1, https://host2.
  • Be careful with links without path: its absence in <data /> is understood by the OS as an opportunity to pass any value. Your application may start processing things it shouldn’t.

That’s all for today. In the final part of the series we will deal with some super important nuances:

  • Why are path patterns so limited?
  • How does URL dispatching work?
  • Why is the Jetpack Navigation Component not as cool as it seems?
  • How we customized deep link processing?
  • And why it’s important to keep deep link backwards compatibility in mind.

See you in part 5!

--

--

Valery Petrov
Valery Petrov

Written by Valery Petrov

Master in Software Engineering, The Institute of Information Technology and Intelligent Systems (ITIS), Kazan Federal University (KFU)

Metakratos Studio
Metakratos Studio

Published in Metakratos Studio

Digital Power for Startups and Enterprises to Create Products of the Future