02 December 2018
Android App Links is a feature designed to reduce friction for users who want to access your content — instead of a user clicking an HTTP link and navigating to your website, Android will redirect them to the same content in your app, for a seamless transition to a native Android experience.
Last month, I implemented Android App links for art collector, a small app which displays a selection of paintings from the Harvard Art Museum API.
It has three main screens:
Gallery | Artist Gallery | Painting |
---|---|---|
I created a sub-domain, https://art-collector.ataulm.com
, for my website. If someone clicks on a link starting with that hostname, then it should open the art collector app!
If someone clicks on a link to specific content (e.g. https://art-collector.ataulm.com/22730
) the app should navigate to that content.
Let’s say:
https://art-collector.ataulm.com
will open the app at the main galleryhttps://art-collector.ataulm.com/{artist_id}
will open an artist galleryhttps://art-collector.ataulm.com/{artist_id}/{painting_id}
will open a specific paintingart collector is modularised by feature — there’s a single base module, app, which has the code for the main gallery, and there are two feature modules (artist and painting) which depend on app.
Since the main gallery (in app) doesn’t know about the artist gallery screen, and doesn’t know about the painting screen, we have to cheat a little to navigate:
fun artistGalleryIntent(artistId: String): Intent {
val uri = Uri.Builder()
.scheme(SCHEME)
.authority(AUTHORITY)
.path(artistId)
.build()
return Intent(Intent.ACTION_VIEW)
.setClassName(
BuildConfig.APPLICATION_ID,
"com.ataulm.artcollector.artist.ui.ArtistActivity"
)
.setData(uri)
}
We can use the component name of a given activity to navigate to it without a reference to the class itself. At the time, I thought this was going to be a temporary hack.
I wanted to navigate only with URLs (hence the Uri
format to send the artistId
as part of the path); as far as the base feature module is concerned, there are no other modules, and it’s just linking out to external websites.
That was the dream.
In reality, it’s not feasible because Android only has limited support for regex in android:pathPattern
. Specifically, it can’t exclude patterns, so host/{artist_id}/{painting_id}
and host/{artist_id}
would be equivalent—in our case, we want to associate these with distinct activity classes.
So actually, we could navigate internally with the component names and intent extras, no need for URIs:
private const val INTENT_EXTRA_ARTIST_ID = "${BuildConfig.APPLICATION_ID}.ARTIST_ID"
fun Intent.artistId() = getStringExtra(INTENT_EXTRA_ARTIST_ID)
fun artistGalleryIntent(artistId: String): Intent {
return Intent(Intent.ACTION_VIEW)
.setClassName(
BuildConfig.APPLICATION_ID,
"com.ataulm.artcollector.artist.ui.ArtistActivity"
)
.putExtra(INTENT_EXTRA_ARTIST_ID, artistId)
}
I raise this to highlight that, internally, your app can do whatever it likes to navigate between screens.
The first step to achieving our goal (frictionless navigation from an external weblink into our app) is associating art collector with the URLs we want to capture.
This PR adds a new DeepLinkActivity
which will declare support for all the art-collector.ataulm.com
links. It checks the complete Uri
in its onCreate(...)
function and navigates to the correct screen.
Support for deep links gets us pretty close to our goal!
As shown in the above recording, the user is going to be faced with a “disambiguation dialog” — not quite the frictionless experience we’re after.
The difference between deep links and Android App Links is that the latter will automatically open the app without showing the dialog.
For this to work, Android needs to know that our app is allowed to consume the links it’s trying to consume — that is, we need to verify that we own the domain.
The verification is simple in theory — we generate a file, and upload it to our website to prove that we own it.
On the Statement List Generator, we fill in the hosting site domain and the app package name:
and we can generate the app package fingerprint with the Java keytool
using our app signing key:
$ keytool -list -v -keystore my-signing-key.keystore
Once we upload the file to our website (https://art-collector.ataulm.com/.well-known/assetlinks.json
) then we’re almost good-to-go.
One final thing that we need to do is add android:autoVerify="true"
:
<activity android:name=".DeepLinkActivity">
<intent-filter android:autoVerify="true">
...
Check out the full PR on GitHub.
and a recording of Android App Links in action:
Android App Links is a feature that’s available on Android 6.0 devices and higher. The implementation shown above won’t break on older devices — it will just offer users the disambiguation dialog to choose between art collector and Google Chrome.
Does it make sense to add Android App Links? If the app can render the same content as the website and offer a better experience, then absolutely! But don’t force users into the app if it’s not as good.
If you have any questions about this post, please write below, ask me on Twitter, or feel free to comment on any of the pull requests on art collector!