この記事はデベロッパー プログラム エンジニア、Benjamin Baxter による Android Developers Blog の記事 "Video Playback with the Google Assistant on Android TV" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。

TV アプリで Google アシスタントを使う方法


今年の初めに、Google アシスタントが Android TV にやってくるとお知らせしておりましたが、それがついにやってきましたAndroid TVGoogle アシスタントを使って、メディア コンテンツの検索、起動、制御が可能になり、さらには電球などのスマート デバイスも制御できるようになります。アシスタントは、ユーザーが TV とインタラクションを行っていることを理解しているので、お気に入りの映画やテレビ番組を見る際に最高の体験を提供できるようになります。

Google アシスタントには、「Mr. インクレディブルを見せて」といったコマンドを理解する機能が組み込まれています。また、一時停止や早送りといったメディアの制御も可能です。本記事では、アプリで Google アシスタントを使う方法を説明します。

Google アシスタントを使うために新しい API は必要ありません。Google アシスタントがアプリに期待するパターンに従うだけで大丈夫です。API とアシスタントを試してみたいという方は、github からこちらのサンプルをダウンロードしてください。

検出


Google アシスタントでは、Android TV の情報を検索しやすくするために、いくつかの変更が行われています。

Google アシスタント経由でコンテンツをユーザーに公開するには、いくつかの方法があります。

サーバーサイドでの統合(登録と導入設定が必要


Google にコンテンツ カタログを提供する必要があります。アプリ外部の Google アシスタントがこのデータを取り込み、利用します。

これは、Google アシスタントに特有の機能ではありません。同じことは、Google 検索、Google Play、Google ホームアプリ、Android TV での検索など、他の Google サービスでも可能です。

クライアントサイドでの統合(すべてのアプリで利用可能)

アプリが既に検索可能である場合は、EXTRA_START_PLAYBACK フラグを処理すればよいだけです。これについては、後ほど詳しく説明します。検索結果でアプリ名が明示的に指定されている場合や、ユーザーが既にアプリを開いている場合は、コンテンツが自動再生されます。

アプリが検索可能になった後は、アシスタントに尋ねるとテストすることができます。騒音の多い場所にいる方は、次の adb コマンドを実行すると、声を出さずにテストできます。
adb shell am start -a "android.search.action.GLOBAL_SEARCH" --es query \"The Incredibles\" 

検索クエリに応答したアプリにつき 1 行の検索結果が表示されます。検索クエリに一致したコンテンツの中に、YouTube とサンプルアプリの Assistant Playback の列があることに注意してください。

「Big Buck Bunny を再生して」というような具体的な検索を行うと、アシスタントは厳密に検索クエリに一致した各アプリのボタンがついたカードを提示します。下のスクリーンショットには、Big Buck Bunny を見るオプションとして、サンプルアプリの Assistant Playback が表示されています。


コンテンツの再生を開始するために、Google アシスタントが直接アプリを起動することもあります。これが起こるのは、コンテンツがそのアプリにしかない場合です。たとえば、「Netflix オリジナルのハウス・オブ・カードを再生して」というような場合です。

起動


ユーザーが検索結果から動画を選択すると、アプリに intent が送信されます。intent アクションの優先順位は、次のとおりです。
  1. 検索から返されたカーソルで指定されている Intent(SUGGEST_COLUMN_INTENT_ACTION
  2. searchable.xml ファイルで searchSuggestIntentAction 値が指定された Intent
  3. デフォルトの ACTION_VIEW


また、アシスタントは、即時再生するかどうかを伝えるために、エクストラを渡します。アプリは、この intent を処理する際に、EXTRA_START_PLAYBACK という boolean 型のエクストラに対応する必要があります。
import static android.support.v4.content.IntentCompat.EXTRA_START_PLAYBACK;

public class SearchableActivity extends Activity {

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       if (getIntent() != null) {
           // Retrieve video from getIntent().getData().

           boolean startPlayback = getIntent().getBooleanExtra(EXTRA_START_PLAYBACK, false);
           Log.d(TAG, "Should start playback? " + (startPlayback ? "yes" : "no"));

           if (startPlayback) {
               // Start playback.
               startActivity(...);
           } else {
               // Show details for movie.
               startActivity(...);
           }
       }
       finish();
   }
}

この動作は、次の adb コマンドを変更して実行するとテストできます。アプリにカスタム アクションがある場合は、 android.intent.action.VIEW をカスタム アクションで置き換えてください。また、-d 引数の値は、アシスタントのクエリから返す URI で置き換えてください。
adb shell 'am start -a android.intent.action.VIEW --ez
android.intent.extra.START_PLAYBACK true -d <URI> -f 0x14000000'

-f 引数は、FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP から得られる論理 OR の値です。これによって、強制的にアクティビティを新しく起動させることができます。

たとえば、次のコマンドを実行すると、サンプルアプリから「Big Buck Bunny」を再生できます。これは、アシスタントから起動した場合と同じ挙動になります。
adb shell 'am start -a android.intent.action.VIEW --ez
android.intent.extra.START_PLAYBACK true -d 
content://com.example.android.assistantplayback/video/2 -n
com.example.android.assistantplayback/.SearchableActivity -f 0x14000000'

上の URI は、searchable.xmlandroid:searchSuggestIntentData で定義されている値(content://com.example.android.assistantplayback/video/)と、クエリから返される SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID の値(2)を組み合わせたものです。

なお、Google アシスタントは最大 7 日間 intent をキャッシュする場合があるため、アプリが既に有効でないコンテンツ再生リクエストを受信する可能性があります。intent ハンドラはステートレスに設計するようにし、ディープリンクを処理する際に、以前の情報に依存しないようにしなければなりません。アプリはこの状況にうまく対応する必要があります。解決策の 1 つに、エラー メッセージを表示してメインの activity または関連する別の activity を開くようにする方法があります。

再生


アプリに MediaSession が正しく実装されていれば、特に変更しなくても動作するはずです。
作成者: Blender FoundationCreative Commons Attribution 3.0 ライセンスにより使用許諾されます。

Google アシスタントは、アプリが転送コントロールに対応していることを前提としているので、アプリの MediaSession に対して TransportControls を使ってメディア コマンドを送信します。動画アプリは、可能な限り以下のコントロールをサポートする必要があります。
  • 再生/一時停止/停止
  • 前へ/次へ
  • 巻き戻し/早送り(seekTo() で実装)


MediaSession.Callback を実装すると、これらのコントロールを簡単にフックできます。PlaybackTransportControlGlue を使って動画を再生する場合、すべてのコールバックで glue と MediaSession を同期させる必要があります。そうでない場合は、このコールバックを使ってプレーヤーを同期させます。
public class MyMediaSessionCallback extends MediaSessionCompat.Callback {

   private final PlaybackTransportControlGlue<?> mGlue;

   public MediaSessionCallback(PlaybackTransportControlGlue<?> glue) {
       mGlue = glue;
   }

   @Override
   public void onPlay() {
       Log.d(TAG, "MediaSessionCallback: onPlay()");
       mGlue.play();
       updateMediaSessionState(...);
   }

   @Override
   public void onPause() {
       Log.d(TAG, "MediaSessionCallback: onPause()");
       mGlue.pause();
       updateMediaSessionState(...);
   }

   @Override
   public void onSeekTo(long position) {
       Log.d(TAG, "MediaSessionCallback: onSeekTo()");
       mGlue.seekTo(position);
       updateMediaSessionState(...);
   }

   @Override
   public void onStop() {
       Log.d(TAG, "MediaSessionCallback: onStop()");
       // Handle differently based on your use case.
   }

   @Override
   public void onSkipToNext() {
       Log.d(TAG, "MediaSessionCallback: onSkipToNext()");
       playAndUpdateMediaSession(...);
   }

   @Override
   public void onSkipToPrevious() {
       Log.d(TAG, "MediaSessionCallback: onSkipToPrevious()");
       playAndUpdateMediaSession(...);
   }
}

さらに学習したい方へ


MediaSession や動画アプリについてさらに詳しく学習したい方は、以下の記事やトレーニング ドキュメントをご覧ください。

Android TV で Google アシスタントを試してみるには、サンプルアプリをダウンロードして Android M 以上を実行している Nvidia Shield で実行します。

さらにディスカッションを行いたい方は、コメントを残すか、Twitter でご連絡ください。



Reviewed by Yoshifumi Yamaguchi - Developer Relations Team