Deep Dive Into Deep Link. Part 4. Handling links issues
Problems: links without a scheme, second host, intercepting anything
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):
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:
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:
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 10116There 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:
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):
Let’s look at each in more detail with an example.
Granted:
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.
Add autoLink with value web to TextView:
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.
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.
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.
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.
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:
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.
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:
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!











