Download all the sample code to your computer using EITHER of the following methods:
$ git clone https://github.com/googlecodelabs/app-indexing.git
From Android Studio, import the app-indexing-start directory () from the downloaded code:
File > Open > .../app-indexing/app-indexing-start
You should now have the app-indexing-start project open in Android Studio.
build.gradle
file. You can leave the SHA1 field empty.google-services.json
file into the app/
directory in your project. Follow the rest of the directions in the console. google-services.json
file to configure the application to use Firebase. In the app/
directory of your project, check your build.gradle
file to confirm that the following line is already added at the end:apply plugin: 'com.google.gms.google-services'
Add the App Indexing gradle target to the build.gradle
file found in the app/
directory. Confirm the following line already appears in the build.gradle
file:
dependencies {
...
compile 'com.google.firebase:firebase-appindexing:10.0.0'
...
}
Now that you have configured the project with Firebase, run the sample app by clicking Run in the Android Studio toolbar. The app should launch on your device and display a splash page for the RecipeApp similar to the one below:
The app doesn't do much for now, but soon you will add support for incoming links.
First, you will add support in the app for URLs associated with the public content of its website. The recipe-app.com website and Android app are already structured in a way that supports a common set of links that both platforms can use and open.
You want the app to open on the appropriate recipe screen for any links directed toward recipe-app.com. To do this, add support to handle URLs that match links of the form http://recipe-app.com/recipe/* to open up in the app. Do this by adding an <intent-filter>
to the AndroidManifest.xml file like so:
...
<activity
android:name=".client.RecipeActivity"
android:label="@string/app_name"
android:exported="true"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Holo.NoActionBar">
<intent-filter android:label="@string/app_name" android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with "http://recipe-app.com/recipe" -->
<data android:scheme="http"
android:host="recipe-app.com"
android:pathPrefix="/recipe" />
</intent-filter>
</activity>
...
Verify that the app can correctly handle HTTP links: first launch the app from Android Studio, and then type the following into your terminal:
$ adb shell am start -a android.intent.action.VIEW \ -d "http://recipe-app.com/recipe/pierogi-poutine" com.recipe_app
You should now see a recipe for Pierogi Poutine on your device.
You just made it possible for the app to provide the right app screen for any corresponding recipe-app.com URL passed to it.
To read more about supporting HTTP links in your app, read Support links to your app content.
At this point, you can use the API to write personal content to the on-device index. The Recipe App has a feature that allows users to comment and leave a note on each recipe for future use. By writing user notes to the index, the API makes it possible for users of the app to search through their personal recipe notes in the Google App.
First, write an IntentService
that periodically updates the index with notes for the most up-to-date information. Add the following code to the AppIndexingService.java
file in the project:
public class AppIndexingService extends IntentService {
...
@Override
protected void onHandleIntent(Intent intent) {
ArrayList<Indexable> indexableNotes = new ArrayList<>();
for (Recipe recipe : getAllRecipes()) {
Note note = recipe.getNote();
if (note != null) {
Indexable noteToIndex = Indexables.noteDigitalDocumentBuilder()
.setName(recipe.getTitle() + " Note")
.setText(note.getText())
.setUrl(recipe.getNoteUrl())
.build();
indexableNotes.add(noteToIndex);
}
}
if (indexableNotes.size() > 0) {
Indexable[] notesArr = new Indexable[indexableNotes.size()];
notesArr = indexableNotes.toArray(notesArr);
// batch insert indexable notes into index
FirebaseAppIndex.getInstance().update(notesArr);
}
}
...
}
Allow Google Play Services to call this IntentService
periodically by adding the following to your AndroidManifest.xml
file:
<service android:name=".client.AppIndexingService"
android:exported="true"
android:permission="com.google.android.gms.permission.APPINDEXING">
<intent-filter>
<action android:name="com.google.firebase.appindexing.UPDATE_INDEX" />
</intent-filter>
</service>
Any time a user adds a note to a recipe, the application should add that text to the on-device index so that it shows up in the Google app. The API has many handy builders that you can use for writing personal content to the index. For an added note on the recipe, use noteDigitalDocumentBuilder
. Add the following method to index a note in RecipeActivity.java
, and uncomment a line in the displayNoteDialog()
method where indexNote()
is called:
private void indexNote() {
Note note = mRecipe.getNote();
Indexable noteToIndex = Indexables.noteDigitalDocumentBuilder()
.setName(mRecipe.getTitle() + " Note")
.setText(note.getText())
.setUrl(mRecipe.getNoteUrl())
.build();
Task<Void> task = FirebaseAppIndex.getInstance().update(noteToIndex);
task.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "App Indexing API: Successfully added note to index");
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
Log.e(TAG, "App Indexing API: Failed to add note to index. " + exception
.getMessage());
}
});
}
Now, when a user adds a note to the recipe app, it is also added to the index.
Any time the user deletes a note from the recipe, the application should also remove the note from the on-device index so that it no longer appears in personal search results. You must remove items from the index by their associated URL. This is the same URL used for setUrl
when initially adding the note to the index.
The following .remove()
function in RecipeActivity.java
, shows how a note is removed from the index. Confirm that RecipeActivity.java
contains the following lines:
...
// Deletes or removes the corresponding notes from index.
String noteUrl = mRecipe.getNoteUrl();
FirebaseAppIndex.getInstance().remove(noteUrl);
Any time a user searches in the Google app, you want relevant results from your app's content to be available as autocomplete. To make this possible, log user actions on the public content after adding it to the index.
The FirebaseUserActions.getInstance().start()
function registers that the user started viewing a particular recipe. Add the start()
call below to the onStart
function of the RecipeActivity.java
class. After that, add the getRecipeViewAction()
method.
@Override
public void onStart() {
super.onStart();
if (recipe != null) {
indexRecipe();
FirebaseUserActions.getInstance().start(getRecipeViewAction());
}
}
private void indexRecipe() {
Indexable recipeToIndex = new Indexable.Builder()
.setName(mRecipe.getTitle())
.setUrl(mRecipe.getRecipeUrl())
.setImage(mRecipe.getPhoto())
.setDescription(mRecipe.getDescription())
.build();
FirebaseAppIndex.getInstance().update(recipeToIndex);
}
private Action getRecipeViewAction() {
return Actions.newView(mRecipe.getTitle(),mRecipe.getRecipeUrl());
}
When a user has finished viewing a recipe, relay the completed user action with the following end()
method in the RecipeActivity.java
file:
@Override
public void onStop() {
if (recipe != null) {
FirebaseUserActions.getInstance().end(getRecipeViewAction());
}
super.onStop();
}
This method indicates the end of a user action and allows the API to measure activity duration, such as time spent on a recipe.
You will now also log user actions on personal content— that is, the action of adding a note to a recipe.
For public content, user actions data is uploaded to Google and the builder enables this capability by default. For personal content, you should explicitly pass in the false
argument to the setUpload()
function of the action builder to keep user actions data on the device.
Add the following function to the RecipeActivity.java
file, passing false
as an argument to the setUpload()
function:
private Action getNoteCommentAction() {
return new Action.Builder(Action.Builder.COMMENT_ACTION)
.setObject(mRecipe.getTitle() + " Note", mRecipe.getNoteUrl())
.setMetadata(new Action.Metadata.Builder().setUpload(false))
.build();
}
Add the following code right after addNoteDialog.show()
is called:
...
addNoteDialog.show();
FirebaseUserActions.getInstance().start(getNoteCommentAction());
Be sure to also uncomment corresponding .end()
calls in displayNoteDialog
when a user finishes adding a note, marked with TODO (developer):
comments in the code.
Let's test to see recipes and notes from RecipeApp were correctly written to the index and appear in the Google app. You can find more ways to test your implementation in our developer documentation.
Handle Incoming Links
$ adb shell am start -a android.intent.action.VIEW \ -d "http://recipe-app.com/recipe/pierogi-poutine" com.recipe_app
Public Content and User Actions
Personal Content and User Actions
Add personal content
Remove personal content
The app is now ready to show content in the Google app powered by the Firebase App Indexing API!
If you would like to find out more about Firebase Android App Indexing, please see the full developer documentation.
You can post questions and find answers on Stack Overflow under the firebase-app-indexing
tags.