Android App Indexing

You’ve worked really hard to create a great Android app, building a better viewing experience for your content on mobile. And yet, most users still find themselves on your website, sent by Google, an email link, or another app.

It doesn’t have to be this way! Android App Indexing allows you to connect pages from your website with specific content within your app. Better still, users who have your app installed can open it directly from Google’s mobile search results. In a talk delivered at the Bay Android Dev Group, Juan Gomez walks us through the code you’ll need to implement app indexing in your app, and on your website. It’s a fast and easy way to send more people to your app, so they can enjoy your content in its most immediate and engaging form.


Deep Linking (0:00)

What is deep linking and why do we want it? Deep linking is a way for you to basically match a URI or URL from your app so that every time that identifier appears on Android, the OS will know that your app will handle that specific URI/URL format. It’s very useful for companies that have content on the web and also on an app, but want to match the two together. Deep linking captures traffic to your website and directs it back to your app, and this happens at the level of the operating system.

The most important thing to get deep linking working on your app is to have some content that is available both on the web and in your app. This is the content that you will want to tie together, so that users will see the information they were expecting to see and not a launcher or launch animation. In addition to the content, you need to identify what those links are going to be, or the URL/URI schema.

2. Add Intent Filter to your Android App (7:29)

Every time you install an app, the operating system goes to the Android manifest and does specific things during installation, including registering intent filters. When you add an intent filter to your app, you’re telling the OS to call the app everytime it sees certain URLs. In your AndroidManifest.xml file, add one or more <intent-filter> elements for the activities that should be launchable from deep links. Add an <action> tag that specifies the ACTION_VIEW intent action, which tells Android that this is an entry point to your app.

Add <data> tags for each data URI format the activity accepts. This is the primary mechanism to declare the format for your deep links. If your website has an intricate URL schema, you can tune this to the granularity you need so that only certain URLs get taken into the app.

<activity
    android:name=".activities.SchemeStartActivity"
    ...
    >
    <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="http"
            android:host="www.eventbrite.com"
            android:path="/e"
            android:pathPattern=" /e/ .*"/>
    ...

You’ll also add a <category> for both BROWSABLE and DEFAULT intent categories. BROWSABLE is required in order for the intent to be executable in a web browser from things like emails and not just Google search results. DEFAULT declares that your app can accept an implicit intent, but is not required if you’re only providing deep links from Google Search results.

3. Add Code to Handle the Intent Filter (11:25)

To handle that, you add an intent filter to let the OS know that the URL you’re expecting has been clicked. In the activity that you register, you call getIntent() in your onCreate, and verify that the action is action view. That way you know that was the launch point for the app and not anywhere else.

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_recipe);
  ...
  Intent intent = getIntent();
  String action = intent.getAction();
  String data = intent.getDataString();
  if (Intent.ACTION_VIEW.equals(action) && data != null) {
    String eventId = data.substring(data.lastIndexOf("/") + 1);
    Uri contentUri = EventContentProvider
            .CONTENT_URI.buildUpon()
            .appendPath(eventId).build();
    showEvent(contentUri);
  }
}

4. Test the Intent Handler (13:34)

You have two ways of testing your deep linking. You can test through ADB by doing an ADB shell and giving it the URL you’re expecting — the app should come up in the emulator, or on your phone if it is connected. If you don’t want to use ADB, Google has provided a testing site, where you can submit the URL to generate a QR code that should take you into the app if everything is working correctly.

App Indexing (14:57)

The problem is that, when you start testing this in the field, the chooser dialogue appears because the content can be handled by multiple things, including your app and browsers. The way to avoid doing that is by using app indexing. App indexing is the second part of deep linking, where you link that URI/URL between Google and your app. Even when users do a Google search, search results can bring them back to the app.

1. Create the URL format for app indexing (16:32)

Creating the URL format is very simple. You use the same URL as deep linking, but to the beginning of that you want to add on android:app://com.app_package/protocol/URL. This reflects the protocol, scheme, package ID, and host path that the app will recognize.

2. Add App Indexing Markup to your website (18:32)

Once you’ve created that URL format, you have to add app indexing markup to your website. For each of the deep links identified earlier, we need to add additional markup. In the HTML, you add a tag that won’t show on the site but has basically the same URL. This markup allows the Google crawler to see that HTML and realise that the site is also using app indexing; it uses those deep links to send users to your Android app. For most modern web application frameworks, this should only involve changing a single template.

3. Verify your website on Webmaster Tools (20:43)

If your website is brand new, you’ll want to verify your site through Webmaster Tools. That’s how the Google crawler knows that it’s there and can index it to do everything it needs to do. In the Google Webmaster Tools, just send a verification request message (example: “Google Play: Link http://www.yourwebsite.com to Android application com.yourpackage.name”), then review and approve the information in the dialog.

4. Connect your app using Google Play Console (21:43)

Next, you’ll want to connect your app using the Google Play Console so the app indexing starts working. If you go to your app, there’s a menu that says “Services and API” in which you can click “Verify Website”, and provide the URL to check that it has the appropriate tags in the HTML. Once that’s all set up, it will start showing in search results.

Search Autocomplete (25:45)

This third part is more of a nice thing that Google is currently doing: allowing you to surface content that’s within your app to Google local results. When you open the search app on your phone, you’ll see that it auto-completes with recent searches, and so you can take advantage of that.

1. Implement App Indexing - Done!

The first prerequisite to doing this is having app indexing, which has been covered already.

2. Verify Google Play Services (27:53)

The next thing we need to do is make sure that Google Play Services is enabled for our app, which is super simple if you’re using Gradle. The App Indexing API requires Google Play Services version 5.+ and minSdkVersion 10 or above. If you’re using selective API on GPS, you’ll want to include in your dependencies (for the version you want to use): com.google.android.gms:play-services-appindexing:7.0.0.

3. Create an API Client (30:04)

Now in your code, we know what happens when people click on the link to display the page. At that point, you need to create a Google Play Services API client. The following code is just one way of doing it, where you can save it on a field on your activity so that you have access to it. You can selectively add which API you’re going to use with that client, and in this case it’s the API for app indexing.

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_recipe);

  mClient = new GoogleApiClient.Builder(this)
            .addApi(AppIndex.APP_INDEX_API).build();
  ...
}

4. Record a Page View (30:56)

Once the page loads, you can connect the client to connect Google Play services. You get the base URI, call app indexing for that view, and give to the client the URLs you created, and the activity. That will return a pending result, which can be used if you want to test for success.

@Override
public void onStart() {
  super.onStart();
  // connect your client
  mClient.connect();

  // define a title for your current page,
  // shown in autocompletion UI
  final String TITLE = event.getTitle();
  final Uri APP_URI = BASE_APP_URI.buildUpon()
          .apppendPath(event.getId()).build();
  finalUri WEB_URL = Uri.parse(event.getUrl());

  // call the app indexing API view method
  PendingResult<Status> result = AppIndex.AppIndexApi
          .view(mClient, this, APP_URI, TITLE, WEB_URL, null);
}

5. Record a Page View End (32:22)

When the user is exiting the content, all you have to do is call viewEnd to mark that session as ending. Using that same Google Services client, you call m.Client.disconnect() and then the pageview gets recorded.

6. Test (32:55)

The nice thing about this is that once your app is set up, it will show up as an area of search. Every time you do a local search on your phone, you’ll see results that come from your app. You can unselect if you don’t want those results, but it turns on by default, an advantage for the app developer.

Q: If I want a certain result to show up, does that information have to be on the title? What determines the results that surface?
Juan: Basically, you have to have already seen the content for it to appear in your search. That’s the disadvantage now, since the search won’t turn up just anything that’s available on your app but something the user already saw. That’s why you register those pageviews. Unfortunately, it’s not smart enough to look into the web URL to look at the content.

Q: I assume this works on Google TV, and Android TV because it’s Android. What about Chromecast or iOS?
Juan: That’s a question we asked Google early on, and they don’t know yet. They don’t have it on iOS yet or anywhere else. I think Android TV should work, but I haven’t really tried it.

Q: If you’ve implemented app indexing, what’s the experience for someone who doesn’t have the app installed? Will they be forced to install it?
Juan: Right now, nothing really happens. If they don’t have the app installed, they’ll get taken to the website all the same and nothing changes. My hope is that the sites and apps that do app indexing will show higher on search results, so if you have app indexing your SEO (Search Engine Optimization) will improve.

Q: Deep linking takes someone to the app if they already have it installed, but can it take them to the Play Store if they don’t?
Juan: You can do that on the website, and there’s many ways to do that. In iOS, they have banners that you can put in Safari so that people can download the app. But that has to be done on the website, there’s nothing your app can do. There’s also many ways to do that on Android, depending on your specific use case.

Q: What happens if there are multiple applications on the phone that can handle the same URL pattern?
Juan: If you’re doing deep linking, you’re going to see the chooser dialogue, which will show you all the apps that are available to handle that. If you’re doing app indexing, that’s a different story because that’s just going to launch the app. On the URL, you embed the package ID so that the OS knows exactly which app to launch. That’s the big advantage with app indexing. I’m not sure what happens if two people register the same exact URL, but you would probably see some sort of error.

Q: Have you run into any other content discovery services, Facebook being notable, where you can perform similar proactive registration of content view and gain results like that?
Juan: I only covered the Google side because it’s the easiest to get started on Android. Facebook has something called App Links which is their version of this. It’s multi-platform, but covering how to implement it and how it works is another talk.

Q: Google’s doc on app indexing has guidelines what to do and what not to do. What was your experience in practice in navigating through those, and what were the developer challenges?
Juan: At Eventbrite it was different becuase we had a Google rep who asked us for an APK and told us what we couldn’t do. But I think if you look at it, the guidelines are reasonable. Everything I’ve said also stays within those guidelines.



Juan Gomez

Juan Gomez

Juan is a Senior Software Engineer at Netflix, where he’s in charge of the design and implementation of various aspects of the company's Android applications. He was previously an Android Engineer at Eventbrite and has been developing mobile solutions since the days of the PDA. He is an active member of both the Python and Android communities, and has previously spoken about Mobile Development topics including Android on conferences like Droidcon, AnDevCon, PyCon US, Appsworld, Mobile+Web DevCon, among others.