Implementing Firebase App Indexing

PreviousTable of ContentsNext
Firebase App IndexingA Firebase App Indexing Tutorial



While the previous chapter took a high level approach to explaining Firebase App Indexing, this chapter will begin to outline in practical terms how the various app indexing features are implemented for an Android app.

Public Content Indexing

As discussed in the previous chapter, public content indexing requires matching website and app content, both accessible using the same URL structure. Assuming that this requirement has been met, public content indexing is primarily a matter of linking the website with the app. This is achieved by placing a JSON configuration file on the web site containing information about the companion app in the form of a digital asset link. The JSON file must be named assetlinks.json and located in the .well-known directory of the web site.

A digital asset link comprises a relation statement granting permission for a target app to be launched using the web site’s link URLs and a target statement declaring the companion app package name and SHA-256 certificate fingerprint. A typical asset link file might, for example, read as follows:

[{
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target" : { "namespace": "android_app",
      "package_name": "<app package name here>",
                 "sha256_cert_fingerprints": ["<app certificate here>"] }
}]  

The assetlinks.json file can contain multiple digital asset links, potentially allowing a single web site to be associated with more than one companion app.

When the app is launched as the result of a link click, the Android app link mechanism is used to launch the app and pass through the URL. Much like Firebase Dynamic Links, the target app must be prepared to handle the app link intent. The first step involves adding an intent filter for the target activity within the project AndroidManifest.xml file:

<activity android:name=".LandmarkActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:host="example.com"
            android:pathPrefix="/computers"
            android:scheme="http" />
    </intent-filter>
</activity>

When the app is launched, the intent needs to be handled and the URL parsed to ensure that the correct content is presented to the user. The URL is delivered as a string stored in the data payload of the intent and can be extracted using code similar to the following:

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

        Intent intent = getIntent();
	String action = intent.getAction();
	String data = intent.getDataString(); // Get the URL

	if (Intent.ACTION_VIEW.equals(action) && data != null) {
		// Code here to parse URL and display content
	}
}

Personal Content Indexing

Personal content indexing is performed by making calls to the Firebase Indexing API. Content items are added to the index on the device in the form of Indexable objects which are created using either Indexable.Builder or one of the preconfigured Builders provided by the Indexables class. The following code creates an Indexable object for a content entry for a vintage computer system using the standard builder:

Indexable indexableComputer = new Indexable.Builder()
       .setName("Atari ST")
       .setUrl("http://www.example.com/computers/atarist")
       .setDescription("Developed after Jack Tramiel bought Atari")
       .build();

Alternatively, one of the many convenience methods provided by the Indexables class can be used. Preconfigured Builder methods are available for creating Indexable objects for content such as documents, messages and notes. Regardless of the convenience builder used, the Indexable object must include name and URL properties. The full list of convenience methods can be found online at:

https://firebase.google.com/docs/reference/android/com/google/firebase/appindexing/builders/Indexables

The following code fragment uses the noteDigitalDocumentBuilder convenience method to create an Indexable object for a user note within the app:

Indexable indexableNote = Indexables.noteDigitalDocumentBuilder()
        .setUrl("http://www.example.com/computers/atarist/comment")
        .setName("Atari ST Personal Notes")
        .setText("Three in good condition on eBay")
        .setImage("http://www.excample.com/images/atariST.png")
        .setDateCreated(creationDate)
        .build();

Once an Indexable object has been created for the content item, it needs to be added to the app index on the device. This involves obtaining a reference to the FirebaseAppIndex instance and a call to its update() method passing through one or more Indexable objects. Since the update is performed asynchronously, the method call returns a Task object onto which listeners may be attached to track the success of the request:

Task<Void> task = FirebaseAppIndex.getInstance().update(indexableComputer);

task.addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        // Content added to index
    }
});

task.addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception exception) {
        // Failed to add content to index
    }
});


App Indexing Service

Indexable objects can, of course, be created and added to the index at any time in the app lifecycle. A useful option, however, is to implement an app indexing intent service within the app to automatically add new content to the index and to rebuild the index in the event of an app upgrade or a corruption of the index. The app indexing service takes the form of an Intent Service which has been assigned app indexing permission and is configured to respond to UPDATE_INDEX intents. When implemented, Google Play services will call the app indexing service under the following conditions:

• After the app is installed on the device.

• If a version of the app that does not make use of app indexing is upgraded to a version that does.

• At periodic intervals to ensure that the index is kept up to date.

When called, it is the responsibility of the app indexing service to create Indexable objects for all of the personal content associated with the app and to perform an update operation using the FirebaseAppIndex instance. The steps to implementing an app indexing service are included in the next chapter entitled A Firebase App Indexing Tutorial.

Logging User Actions

Logging of user actions is, once again, divided into public and personal actions. User actions are represented by instances of the Action class built using Action.Builder and specifying an appropriate action type. A wide range of types are available for actions including commenting, sending, sharing viewing, liking, adding and bookmarking. For a full list of types, refer to the following web page:

https://firebase.google.com/docs/reference/android/com/google/firebase/appindexing/Action.Builder

The following code, for example, creates a view Action object when a user views specific content within the app:

Action action = new Action.Builder(Action.Builder.VIEW_ACTION)
    .setObject("Amiga 500", "http://www.example.com/computers/amiga500")
    .build();

Alternatively, the same Action object can be created using the newView() convenience method of the Actions class:

Action action = Actions.newView("Amiga 500", 
		"http://www.example.com/computers/amiga500")

When the action being logged is associated with personal content, metadata must be included within the action to indicate that the action should not be uploaded to Google:

Action action = new Action.Builder(Action.Builder.VIEW_ACTION)
     .setObject("Amiga 500", "http://www.example.com/computers/amiga500")
     .setMetadata(new Action.Metadata.Builder().setUpload(false))
     .build();

Once the Action object has been built, it needs to be logged using the FirebaseUserActions instance. For actions that take time the start() and end() methods of the FirebaseUserActions instance should be called at the beginning and end of the action respectively, passing through the Action object in each call:

FirebaseUserActions.getInstance().start(action); // User action starts

FirebaseUserActions.getInstance().end(action); // User action ends

If the action does not take place over a period of time, simply call the end() method:

FirebaseUserActions.getInstance().end(action); // User action ends

Note that before an action can be logged, the content must already be indexed. When indexing content and logging an action at the same time, therefore, it is important to complete the indexing operation before logging the action.

As previously mentioned, user action logging is used in part to allow the Google app to provide autocomplete suggestions when entering a search query. To include public content in the autocomplete list, index the public content as though it is personal content before logging the public action.

Removing Index Entries

When a user removes personal content from within an app, the matching index entry must also be removed from the index. Individual index entries are deleted by calling the remove() method of the FirebaseAppIndex instance, passing through as an argument the URL associated with the content, for example:

FirebaseAppIndex.remove("http://www.example.com/computers/atarist/comment");

To remove all the index entries on the device, make a call to the removeAll() method:

FirebaseAppIndex.removeAll();

This can be useful if the index entries need to be removed when the user logs out of the app. Assuming that the app indexing service has been implemented, the app index entries belonging to the user will be re-instated next time the user accesses the app.

Summary

Implementation of public content app indexing is a multi-step process that involves the structuring of content within the app to match that of the corresponding web site and then notifying Google through a digital assets link file that the app and web site content are related. The indexing of personal content involves the creation of Indexable objects in conjunction with the update() method of the FirebaseAppIndex instance. Actions are logged by building Action objects and logging them via the start() and end() methods of the FirebaseUserActions instance. The personal content index should also be kept up to date through the implementation of an App Indexing intent service.




PreviousTable of ContentsNext
Firebase App IndexingA Firebase App Indexing Tutorial