ブラウズ可能な検索結果を表示する

すべてのアプリが音声検索をサポートする必要があります。このページでは、音声なしで検索を開始できるようにし、検索結果のリストを表示してユーザーが別の結果を選択できるようにすることで、検索エクスペリエンスをさらに向上させる方法について説明します。たとえば、提案された結果が最も関連性の高いものではない場合などです。

メディアアプリは、Android Auto と Android Automotive OS(AAOS)でコンテキスト検索結果を提供できます。これらの結果は、ユーザーが検索クエリを開始したとき、または最新の検索結果を表示したときに表示されます。

これらの検索結果を有効にして提供するには:

  • サービスの onGetRoot メソッドで検索サポートを宣言します。

  • メディア ブラウザ サービスの onSearch メソッドをオーバーライドして、ユーザーの検索キーワードを処理します。

  • タイトル アイテムを使用して検索結果を整理し、ブラウズしやすくします。

アプリは、検索クエリが開始されたときに表示されるコンテキスト検索結果を提供できます。Android Auto と AAOS は、検索クエリ インターフェースや、前に行われたクエリで決まるアフォーダンスを通じて、検索結果を表示します。詳しくは、音声操作をサポートするをご覧ください。

ユーザーの音声検索に関連するメディア アイテムを表示できる [**検索結果**] オプションが表示された再生ビュー

図 1. ユーザーの音声検索に関連するメディア アイテムを表示できる [検索結果] オプションが表示された再生ビュー。

アプリが検索結果の表示をサポートしていることを示すには、サービスの onGetRoot メソッドから返される extras バンドルに定数キー BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED を含めて、ブール値 true にマッピングします。

Kotlin

import androidx.media.utils.MediaConstants

@Nullable
override fun onGetRoot(
    @NonNull clientPackageName: String,
    clientUid: Int,
    @Nullable rootHints: Bundle
): BrowserRoot {
    val extras = Bundle()
    extras.putBoolean(
        MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true)
    return BrowserRoot(ROOT_ID, extras)
}

Java

import androidx.media.utils.MediaConstants;

@Nullable
@Override
public BrowserRoot onGetRoot(
    @NonNull String clientPackageName,
    int clientUid,
    @Nullable Bundle rootHints) {
    Bundle extras = new Bundle();
    extras.putBoolean(
        MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true);
    return new BrowserRoot(ROOT_ID, extras);
}

検索結果を提供するには、メディア ブラウザ サービスの onSearch メソッドをオーバーライドします。Android Auto と AAOS は、ユーザーが検索クエリ インターフェースまたは検索結果アフォーダンスを呼び出すと、ユーザーの検索キーワードをこのメソッドに転送します。

検索結果をよりブラウズしやすくするには、タイトル アイテムを使用します。たとえば、アプリで音楽を再生する場合、アルバム、アーティスト、曲ごとに検索結果をまとめます。次のコード スニペットは、onSearch メソッドの実装を示しています。

Kotlin

fun onSearch(query: String, extras: Bundle) {
  // Detach from results to unblock the caller (if a search is expensive).
  result.detach()
  object:AsyncTask() {
    internal var searchResponse:ArrayList
    internal var succeeded = false
    protected fun doInBackground(vararg params:Void):Void {
      searchResponse = ArrayList()
      if (doSearch(query, extras, searchResponse))
      {
        succeeded = true
      }
      return null
    }
    protected fun onPostExecute(param:Void) {
      if (succeeded)
      {
        // Sending an empty List informs the caller that there were no results.
        result.sendResult(searchResponse)
      }
      else
      {
        // This invokes onError() on the search callback.
        result.sendResult(null)
      }
      return null
    }
  }.execute()
}
// Populates resultsToFill with search results. Returns true on success or false on error.
private fun doSearch(
    query: String,
    extras: Bundle,
    resultsToFill: ArrayList
): Boolean {
  // Implement this method.
}

Java

@Override
public void onSearch(final String query, final Bundle extras,
                        Result<List<MediaItem>> result) {

  // Detach from results to unblock the caller (if a search is expensive).
  result.detach();

  new AsyncTask<Void, Void, Void>() {
    List>MediaItem> searchResponse;
    boolean succeeded = false;
    @Override
    protected Void doInBackground(Void... params) {
      searchResponse = new ArrayList&lt;MediaItem>();
      if (doSearch(query, extras, searchResponse)) {
        succeeded = true;
      }
      return null;
    }

    @Override
    protected void onPostExecute(Void param) {
      if (succeeded) {
       // Sending an empty List informs the caller that there were no results.
       result.sendResult(searchResponse);
      } else {
        // This invokes onError() on the search callback.
        result.sendResult(null);
      }
    }
  }.execute()
}

/** Populates resultsToFill with search results. Returns true on success or false on error. */
private boolean doSearch(String query, Bundle extras, ArrayList&lt;MediaItem> resultsToFill) {
    // Implement this method.
}