コンソールへ移動

パーソナル コンテンツのインデックス処理を有効にする

パーソナル コンテンツ インデックス処理では、ユーザーのアカウントに関連付けられたコンテンツに関して、Google アプリの検索結果を自分のアプリから提供できます。ユーザーのデバイスには、検索候補とパーソナル検索結果の両方で、そのユーザーのパーソナル コンテンツだけが表示されます。

たとえば、ユーザーが「お気に入りの鶏肉レシピ」を検索した場合、ユーザーがレシピアプリで鶏肉レシピに追加したメモが [パーソナル検索結果] タブに表示されます。

アプリにパーソナル コンテンツを含めない場合は、この手順をスキップして、ユーザー アクションを記録するに直接進むことができます。

開始する前に、アプリ コンテンツへのリンクをサポートし、アプリに App Indexing ライブラリを追加したことを確認してください。

インデックスを設定してオブジェクトを追加する

JobIntentService を拡張するクラスを作成します。このドキュメントで説明する実装は、JobIntentService クラスに依存してデバイス上のインデックス更新作業をキューに入れますが、別のクラスを使用して作業スケジュールを設定することもできます。JobIntentService クラスを使用して実装する場合は、プロジェクトの build.gradle ファイルに Android Support Library を追加する必要もあります。

次に、項目をアプリのパーソナル コンテンツ インデックスに含めるため、同じクラスで Indexable オブジェクトを作成します。

結果の種類

App Indexing では、次の 2 つの方法で結果を表示できます。

  • テキストのみ: Google 検索では、インデックス登録されたコンテンツがテキストの結果として表示されます。
  • スライス: Google 検索ではスライス、つまりアプリの UI テンプレートを使用して、動的でインタラクティブな検索結果を提供します。

アプリに最適な結果の種類を選択できます。テキストのみの結果は実装が比較的容易ですが、スライスではより表現力豊かなユーザー エクスペリエンスが提供されます。

サンプル実装

テキストのみの結果

Java

public class AppIndexingUpdateService extends JobIntentService {

    // Job-ID must be unique across your whole app.
    private static final int UNIQUE_JOB_ID = 42;

    public static void enqueueWork(Context context) {
        enqueueWork(context, AppIndexingUpdateService.class, UNIQUE_JOB_ID, new Intent());
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        // TODO Insert your Indexable objects — for example, the recipe notes look as follows:

        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);
        }
    }

    // ...
}

Kotlin

class AppIndexingUpdateService : JobIntentService() {

    companion object {

        // Job-ID must be unique across your whole app.
        private const val UNIQUE_JOB_ID = 42

        fun enqueueWork(context: Context) {
            JobIntentService.enqueueWork(context, AppIndexingUpdateService::class.java, UNIQUE_JOB_ID, Intent())
        }
    }

    override fun onHandleWork(intent: Intent) {
        // TODO Insert your Indexable objects — for example, the recipe notes look as follows:

        val indexableNotes = arrayListOf<Indexable>()

        for (recipe in getAllRecipes()) {
            val note = recipe.note
            if (note != null) {
                val noteToIndex = Indexables.noteDigitalDocumentBuilder()
                        .setName(recipe.title + " Note")
                        .setText(note.text)
                        .setUrl(recipe.noteUrl)
                        .build()

                indexableNotes.add(noteToIndex)
            }
        }

        if (indexableNotes.size > 0) {
            val notesArr: Array<Indexable> = indexableNotes.toTypedArray()

            // batch insert indexable notes into index
            FirebaseAppIndex.getInstance().update(*notesArr)
        }
    }

    // ...
}
スライスの結果

スライスを使用すると、Google 検索のクエリへのレスポンスとして表示されるアプリの部分を指定できます。表示される部分は Indexables でコンテンツの UI として機能するため、各スライスを Indexable に直接マッピングする必要があります。アプリ向けのスライス対応の結果を生成するには、次の手順に沿って操作します。

  1. スライスのスタートガイドの手順に沿って、アプリにスライスを追加します。
  2. スライスのメタデータとリスナーが含まれる Indexables を追加します。
package com.example.myapp;

import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.JobIntentService;
import com.google.firebase.appindexing.builders.Indexables;
import com.google.firebase.appindexing.FirebaseAppIndex;
import com.google.firebase.appindexing.Indexable;
import java.util.ArrayList;

public class AppIndexingUpdateService extends JobIntentService {
  // Job-ID must be unique across your whole app.
  private static final int UNIQUE_JOB_ID = 42;

  public static void enqueueWork(Context context) {
    enqueueWork(context, AppIndexingUpdateService.class, UNIQUE_JOB_ID, new Intent());
  }

  @Override
  protected void onHandleWork(@NonNull Intent intent) {
    // TODO Insert your Indexable objects — for example, the recipe notes look as follows:

    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())
                    .setMetadata(
                      new Indexable.Metadata.Builder().setSliceUri(
                        Uri.parse("content://com.example.myapp.sliceprovider/note?id=" +
                                  recipe.getNoteId())
                    ))
                    .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)
                        .addOnSuccessListener(unused -> {
                          Log.d("RecipeApp", "Update succeeded!");
                        })
                        .addOnFailureListener(exception -> {
                          Log.d("RecipeApp", "Update failed: " + exception);
                        });
    }
  }
}

パーソナル コンテンツ インデックスに含めるもの

以下のコンテンツ タイプについて、Indexable オブジェクトを定義します。

  • メッセージ、写真、ドキュメントなどのユーザー固有のコンテンツ。
  • お気に入りや頻繁にアクセスするコンテンツなどの、ユーザーにとって重要なコンテンツ。たとえば、ブックマークされたまたはオフライン使用としてマークされたドキュメントや歌など。
  • アプリでアクセスするだけでなく、アプリ内で生成するコンテンツ。たとえば、スマートフォンの電話帳にある連絡先ではなく、ユーザーが直接アプリで作成して保存した連絡先。

アプリにブロードキャスト レシーバーを追加する

Google Play 開発者サービスはアプリに、デバイス上のインデックスを更新するよう定期的にリクエストを送信します。これにより、デバイス上のインデックスにアプリの最新のコンテンツを含めることができます。BroadcastReceiver クラスはこのリクエストを受け取り、インデックス作業を処理するために JobIntentService をトリガーします。以下の例では、前の手順の AppIndexingUpdateService クラスを使用しています。

Java

/** Receives broadcast for App Indexing Update. */
public class AppIndexingUpdateReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent != null
                && FirebaseAppIndex.ACTION_UPDATE_INDEX.equals(intent.getAction())) {
            // Schedule the job to be run in the background.
            AppIndexingUpdateService.enqueueWork(context);
        }
    }

}

Kotlin

/** Receives broadcast for App Indexing Update. */
class AppIndexingUpdateReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent?) {
        if (intent != null && FirebaseAppIndex.ACTION_UPDATE_INDEX == intent.action) {
            // Schedule the job to be run in the background.
            AppIndexingUpdateService.enqueueWork(context)
        }
    }
}

インデックスを生成して更新する

次に、以下の 3 つの状況下で Google Play 開発者サービスが App Indexing サービスを呼び出してデバイス上のインデックスを更新し、ユーザーのパーソナル コンテンツがインデックスに反映されるようにします。

  • アプリがデバイス上にインストールされるとき
  • アプリの既存のバージョンが、パーソナル コンテンツ インデックス処理をサポートするバージョンに更新された場合
  • インデックス処理されるコンテンツの変更内容を正確に反映し、シームレスなユーザー エクスペリエンスを確保するための定期的な呼び出し

加えて、デバイス上のインデックスがなんらかの理由(インデックスが破損した場合など)で消失した場合は、インデックスを更新するためのこの呼び出しで再生成されます。

AndroidManifest.xml ファイルで BroadcastReceiver を登録し、このレシーバーが Google Play 開発者サービスからの呼び出しを受け取ってインデックスを更新できるようにします。以下の例では、JobIntentService クラスを使用して、インデックス更新のスケジュールを設定しています。

<!-- Register the BroadcastReceiver -->
<receiver
    android:name=".AppIndexingUpdateReceiver"
    android:exported="true"
    android:permission="com.google.android.gms.permission.APPINDEXING">
    <intent-filter>
        <action android:name="com.google.firebase.appindexing.UPDATE_INDEX" />
    </intent-filter>
</receiver>

<!-- Grant the AppIndexingUpdateService permission and enable it to run after being triggered -->
<service
    android:name=".AppIndexingUpdateService"
    android:permission="android.permission.BIND_JOB_SERVICE" />

インデックスを更新する

ユーザーがパーソナル コンテンツを追加、更新、または削除した際は、デバイス上のインデックスにそれらの変更を反映する必要があります。インデックス内のコンテンツを更新するには、次のコードを追加します。

テキストのみを更新する

Java

public void indexNote(Recipe recipe) {
    Note note = recipe.getNote();
    Indexable noteToIndex = Indexables.noteDigitalDocumentBuilder()
            .setName(recipe.getTitle())
            .setText(note.getText())
            .setUrl(recipe.getNoteUrl())
            .build();

    Task<Void> task = FirebaseAppIndex.getInstance().update(noteToIndex);
    // ...
}

Kotlin

fun indexNote(recipe: Recipe) {
    val note = recipe.note
    val noteToIndex = Indexables.noteDigitalDocumentBuilder()
            .setName(recipe.title)
            .setText(note!!.text)
            .setUrl(recipe.noteUrl)
            .build()

    val task = FirebaseAppIndex.getInstance().update(noteToIndex)
    // ...
}
スライスを更新する

Firebase App Indexing では、インデックス登録時にスライスのコンテンツを取得して保存し、クエリ時にスライスのスナップショットを表示します。インデックス登録とクエリの間でスライスのコンテンツが変更されると、そのスナップショットは、ユーザーの誤解を招いたりユーザーを混乱させたりする可能性があります。これを回避するには、スライスの更新と Indexable の再インデックス登録を同時に行い、Firebase App Indexing でスライスのコンテンツが常に最新になるようにします。

private void indexNote() {
  Note note = mRecipe.getNote();
  Indexable noteToIndex = Indexables.noteDigitalDocumentBuilder()
          .setName(mRecipe.getTitle())
          .setText(note.getText())
          .setUrl(mRecipe.getNoteUrl())
          .setKeywords("recipe", "cooking")
          .setMetadata(
              new Indexable.Metadata.Builder().setSliceUri(
              Uri.parse("content://com.example.myapp.sliceprovider/note?id=" + note.getNoteId())))
          .build();

  Task<Void> task = FirebaseAppIndex.getInstance().update(noteToIndex);
   ...
}
削除

削除する項目を特定するには、その項目の URL を使用します。次の行を、アプリのその項目に関する delete または remove 関数に追加します。

Java

// Deletes or removes the corresponding notes from index.
String noteUrl = recipe.getNoteUrl();
FirebaseAppIndex.getInstance().remove(noteUrl);

Kotlin

// Deletes or removes the corresponding notes from index.
val noteUrl = recipe.noteUrl
FirebaseAppIndex.getInstance().remove(noteUrl)

ユーザーのログアウト時にインデックスを削除する

ユーザーがアプリからログアウトしたとき、ログアウト イベントでデバイス上のインデックスを削除します。この場合、ユーザーが再びログインしたときにデバイス上のインデックスが再生成されるように、AppIndexingService を使用してインデックスを生成して更新する呼び出しを含めます。

ユーザーがアプリからログアウトしたときにデバイス上のインデックスを削除するには、次のコードをアプリに追加します。

Java

FirebaseAppIndex.getInstance().removeAll();

Kotlin

FirebaseAppIndex.getInstance().removeAll()

次のステップ: ユーザー アクションを記録する