Google アシスタントとメディアアプリ

Google アシスタントを使用すると、以下のようなさまざまなデバイスを音声コマンドで操作できます。 Google Home やスマートフォンなど。Cloud Shell には、 メディア コマンドを理解(「ビヨンセの曲を再生」) メディア コントロール(一時停止、スキップ、早送り、高評価など)

アシスタントは、メディアを使用して Android メディアアプリと通信します。 あります。使用可能な インテントまたはサービスを使用して、 アプリを起動して再生を開始できます。最良の結果を得るには、アプリで このページで説明するすべての機能を実装する必要があります。

メディア セッションの使用

すべてのオーディオ / 動画アプリには、 メディア セッション アシスタントに話しかけて トランスポート コントロールを自動的に呼び出します。

アシスタントはこのセクションにリストされているアクションのみを使用しますが、 すべての Presation API と Playback API を実装して、 他のアプリケーションとの互換性を保ちます。サポート対象外のアクションについては メディア セッション コールバックは、 ERROR_CODE_NOT_SUPPORTED

アプリのコードでこれらのフラグを設定して、メディアとトランスポートのコントロールを有効にします。 MediaSession オブジェクト:

Kotlin

session.setFlags(
        MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
        MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
)

Java

session.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
    MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

アプリのメディア セッションでは、サポートするアクションを宣言し、 対応するメディア セッション コールバックを呼び出せます。サポートされているアクションを setActions()

ユニバーサル Android Music Player サンプル プロジェクトは、メディア セッションの設定方法を示す良い例です。

再生のアクション

サービスから再生を開始するには、メディア セッションに以下の PLAY アクションとそのコールバックが必要です。

アクション コールバック
ACTION_PLAY onPlay()
ACTION_PLAY_FROM_SEARCH onPlayFromSearch()
ACTION_PLAY_FROM_URI(*) onPlayFromUri()

また、以下の PREPARE アクションとそのコールバックも実装する必要があります。

アクション コールバック
ACTION_PREPARE onPrepare()
ACTION_PREPARE_FROM_SEARCH onPrepareFromSearch()
ACTION_PREPARE_FROM_URI(*) onPrepareFromUri()

(*)Google アシスタントの URI ベースのアクションは企業でのみ機能します Google に URI を提供します。Google にメディア コンテンツを説明する方法の詳細 メディア アクションをご覧ください。

準備 API を実装すると、音声コマンド後の再生レイテンシが 減らすことができます再生レイテンシを改善したいメディアアプリは、 コンテンツのキャッシュ保存とメディア再生の準備を開始する時間が長くなります。

検索クエリの解析

ユーザーが特定のメディア アイテムを検索したとき(例: 「ジャズを再生して」 [アプリ名]」「[曲のタイトル] を聴く」onPrepareFromSearch() または onPlayFromSearch() コールバック メソッドがクエリ パラメータとエクストラ バンドルを受け取ります。

アプリは音声検索のクエリを解析し、次の手順で再生を開始する必要があります。 手順:

  1. 音声検索から返されたエクストラ バンドルと検索語句の文字列を使用 結果をフィルタできます
  2. その結果に基づいて再生キューを作成する。
  3. 結果の中から最も関連性の高いメディア アイテムを再生する。
で確認できます。

onPlayFromSearch() メソッドは音声からの詳細情報を含む extras パラメータを受け取ります。 できます。これらのエクストラは、再生するアプリ内の音声コンテンツを見つけるのに役立ちます。 検索結果からこのデータを提供できない場合は、ロジックを実装できます。 未加工の検索クエリを解析し、 なります。

Android Automotive OS と Android Auto では、以下の extras がサポートされています。

次のコード スニペットは、onPlayFromSearch() をオーバーライドする方法を示しています。 MediaSession.Callback のメソッド 実装することで、音声検索クエリを解析して再生を開始できます。

Kotlin

override fun onPlayFromSearch(query: String?, extras: Bundle?) {
    if (query.isNullOrEmpty()) {
        // The user provided generic string e.g. 'Play music'
        // Build appropriate playlist queue
    } else {
        // Build a queue based on songs that match "query" or "extras" param
        val mediaFocus: String? = extras?.getString(MediaStore.EXTRA_MEDIA_FOCUS)
        if (mediaFocus == MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE) {
            isArtistFocus = true
            artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST)
        } else if (mediaFocus == MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE) {
            isAlbumFocus = true
            album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM)
        }

        // Implement additional "extras" param filtering
    }

    // Implement your logic to retrieve the queue
    var result: String? = when {
        isArtistFocus -> artist?.also {
            searchMusicByArtist(it)
        }
        isAlbumFocus -> album?.also {
            searchMusicByAlbum(it)
        }
        else -> null
    }
    result = result ?: run {
        // No focus found, search by query for song title
        query?.also {
            searchMusicBySongTitle(it)
        }
    }

    if (result?.isNotEmpty() == true) {
        // Immediately start playing from the beginning of the search results
        // Implement your logic to start playing music
        playMusic(result)
    } else {
        // Handle no queue found. Stop playing if the app
        // is currently playing a song
    }
}

Java

@Override
public void onPlayFromSearch(String query, Bundle extras) {
    if (TextUtils.isEmpty(query)) {
        // The user provided generic string e.g. 'Play music'
        // Build appropriate playlist queue
    } else {
        // Build a queue based on songs that match "query" or "extras" param
        String mediaFocus = extras.getString(MediaStore.EXTRA_MEDIA_FOCUS);
        if (TextUtils.equals(mediaFocus,
                MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE)) {
            isArtistFocus = true;
            artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST);
        } else if (TextUtils.equals(mediaFocus,
                MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE)) {
            isAlbumFocus = true;
            album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM);
        }

        // Implement additional "extras" param filtering
    }

    // Implement your logic to retrieve the queue
    if (isArtistFocus) {
        result = searchMusicByArtist(artist);
    } else if (isAlbumFocus) {
        result = searchMusicByAlbum(album);
    }

    if (result == null) {
        // No focus found, search by query for song title
        result = searchMusicBySongTitle(query);
    }

    if (result != null && !result.isEmpty()) {
        // Immediately start playing from the beginning of the search results
        // Implement your logic to start playing music
        playMusic(result);
    } else {
        // Handle no queue found. Stop playing if the app
        // is currently playing a song
    }
}

音声検索を実装して音声を再生する詳細な例については、 Universal Android Music Player をご覧ください。 表示されます。

空のクエリの処理

onPrepare()onPlay()onPrepareFromSearch()、または onPlayFromSearch() の場合 が検索クエリなしで呼び出された場合、メディアアプリは「現在の」 できます。現在のメディアがない場合、アプリは次のようなメディアの再生を試みます。 最新のプレイリストまたはランダムなキューから曲として表示します。アシスタントは これらの API は、ユーザーが「[アプリ名] で音楽を再生して」とリクエストした場合に、 追加情報が表示されます。

ユーザーが「[アプリ名] で音楽を再生して」と言った場合、Android Automotive OS または Android Auto は、アプリの onPlayFromSearch() を呼び出して、アプリを起動して音声を再生しようとします メソッドを呼び出します。ただし、ユーザーがメディア アイテムの名前を言わなかったため、onPlayFromSearch() メソッドが空のクエリ パラメータを受け取ります。そのような場合は、 たとえば、最近再生した曲の プレイリストまたはランダムなキューです

以前の音声操作サポートの宣言

ほとんどの場合、上記の再生操作を処理すると、アプリに 制御する必要があります。ただし、システムによっては、 検索用のインテント フィルタが含まれている。このインテントのサポートを宣言する フィルタを使用できます。

スマートフォン アプリのマニフェスト ファイルに、次のコードを追加します。

<activity>
    <intent-filter>
        <action android:name=
             "android.media.action.MEDIA_PLAY_FROM_SEARCH" />
        <category android:name=
             "android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

トランスポート コントロール

アプリのメディア セッションがアクティブになると、アシスタントは音声コマンドを発することができる を使用して再生の制御やメディア メタデータの更新を行います。そのためには、 次のアクションを有効にし、対応する あります。

アクション コールバック 説明
ACTION_SKIP_TO_NEXT onSkipToNext() 次の動画
ACTION_SKIP_TO_PREVIOUS onSkipToPrevious() 前の曲
ACTION_PAUSE, ACTION_PLAY_PAUSE onPause() 一時停止
ACTION_STOP onStop() 停止
ACTION_PLAY onPlay() 再開
ACTION_SEEK_TO onSeekTo() 30 秒巻き戻し
ACTION_SET_RATING onSetRating(android.support.v4.media.RatingCompat) 高く / 低く評価
ACTION_SET_CAPTIONING_ENABLED onSetCaptioningEnabled(boolean) 字幕オン / オフ

注:

  • 移動コマンドを機能させるには、PlaybackStatestate, position, playback speed, and update time が最新状態でなければなりません。アプリでは、状態が変化したときに setPlaybackState() を呼び出す必要があります。
  • メディアアプリでは、メディア セッション メタデータも最新状態に保つ必要があります。これにより、「今かかっている曲は何?」などの質問がサポートされます。アプリでは、該当の項目(トラックのタイトル、アーティスト、名前など)が変わった時点で setMetadata() を呼び出す必要があります。
  • MediaSession.setRatingType() は、アプリがサポートする評価のタイプを示すように設定する必要があり、アプリでは onSetRating() を実装する必要があります。アプリが評価をサポートしていない場合は、評価タイプをRATING_NONEに設定します。

サポートされている音声操作は、コンテンツの種類によって異なる可能性があります。

コンテンツ タイプ 必要な対応
音楽

必須のサポート: 再生、一時停止、停止、次へスキップ、前へスキップ

サポートを強く推奨する: 移動

ポッドキャスト

必須のサポート: 再生、一時停止、停止、移動

次のサポートを推奨: 次へスキップ、前へスキップ

オーディオブック 必須のサポート: 再生、一時停止、停止、移動
ラジオ 必須のサポート: 再生、一時停止、停止
ニュース 必須のサポート: 再生、一時停止、停止、次へスキップ、前へスキップ
動画

サポートが必要: 再生、一時停止、停止、移動、巻き戻し、早送り

次の機能のサポートを強く推奨します: 次へスキップ、前へスキップ

提供しているプロダクトと同数のアクションをサポートする必要があります 他のアクションには適切に応答します。たとえば プレミアム ユーザーが前のアイテムに戻ることができる場合は、 無料枠のユーザーがアシスタントに前のアイテムに戻るよう要求するとエラー 詳しくは、エラー処理のセクションをご覧ください。

試せる音声クエリの例

次の表にいくつかのサンプルクエリの概要を示します。 実装をテストします。

MediaSession コールバック 使用する「OK Google」フレーズ
onPlay()

「再生して」

「再開して」

onPlayFromSearch()
onPlayFromUri()
音楽

(アプリ名)で音楽や曲を再生して」これは空のクエリです。

(アプリ名)(曲 | アーティスト | アルバム | ジャンル | プレイリスト)を再生して」

ラジオ (周波数 | ステーション)(アプリ名)で再生して」
Audiobook

(アプリ名)でオーディオブックを読んで」

(アプリ名)(オーディオブック)を読んで」

ポッドキャスト (ポッドキャスト)(アプリ名)で再生して」
onPause() 「一時停止して」
onStop() 「停止して」
onSkipToNext() 「次へ(曲 | エピソード | トラック)
onSkipToPrevious() 「前(曲 | エピソード | トラック)
onSeekTo()

「再起動してください。」

## 秒早送りして」

## 分戻って」

なし( MediaMetadata 更新) 「何を再生中か教えて?」

エラー

メディア セッションでエラーが発生すると、アシスタントはエラーを処理して、 ユーザーに配布できます。メディア セッションでトランスポート状態を更新して、 PlaybackState 内のエラーコードを正しく読み上げる必要があります。詳しくは、 メディア セッションです。アシスタント 返されたすべてのエラーコードを getErrorCode()

適切に処理されないことが多いケース

確実に処理する必要があるエラーケースの例を 正しく説明します。

  • ユーザーのログインが必要 <ph type="x-smartling-placeholder">
      </ph>
    • PlaybackState エラーコードを ERROR_CODE_AUTHENTICATION_EXPIRED に設定します。
    • PlaybackState エラー メッセージを設定します。
    • 再生に必要な場合は、PlaybackState 状態を STATE_ERROR に設定します。 それ以外の場合は、PlaybackState の残りの部分をそのまま保持します。
  • ユーザーが利用できない操作をリクエストしています <ph type="x-smartling-placeholder">
      </ph>
    • PlaybackState エラーコードを適切に設定します。たとえば、 ERROR_CODE_NOT_SUPPORTED まで PlaybackState アクションがサポートされていない場合、または ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED の場合 アクションがログインで保護されているかどうか。
    • PlaybackState エラー メッセージを設定します。
    • 残りの PlaybackState はそのままにします。
  • ユーザーが、アプリで利用できないコンテンツをリクエストしている <ph type="x-smartling-placeholder">
      </ph>
    • PlaybackState エラーコードを適切に設定します。たとえば、 ERROR_CODE_NOT_AVAILABLE_IN_REGION
    • PlaybackState エラー メッセージを設定します。
    • 再生を中断するには、PlaybackSate の状態を STATE_ERROR に設定します。 それ以外の場合は、PlaybackState の残りの部分はそのままにします。
  • ユーザーが完全一致を利用できないコンテンツをリクエストしている。たとえば、 無料枠のユーザーが、プレミアム階層のユーザーしか利用できないコンテンツを要求している。
    • エラーを返さずに、代わりに優先する 似たようなものを見つけますアシスタントが一番多くの話し声を処理します 音声レスポンスを出力する必要があります。

インテントを使用した再生

アシスタントは音声または動画アプリを起動し、 ディープリンクを使ってインテントを作成します。

インテントとそのディープリンクは、以下のさまざまなソースから取得できます。

  • アシスタントが モバイルアプリを起動すると、Google 検索を使用して、 は、リンクを含むウォッチ アクションを提供します。
  • アシスタントが TV アプリを起動する場合、アプリには テレビ検索プロバイダ メディア コンテンツの URI を公開します。Google アシスタントがクエリを コンテンツ プロバイダ。ディープリンクの URI を含むインテントが返されます。 オプションのアクションです クエリでアクションが返された場合、 アシスタントはそのアクションと URI をアプリに返します。 プロバイダが指定しなかった場合 ACTION_VIEW をインテントに追加します。

アシスタントが、値が trueEXTRA_START_PLAYBACK を追加する 渡します。アプリは次の場合に再生を開始します。 EXTRA_START_PLAYBACK を含むインテントを受け取る。

アクティブ時のインテントの処理

ユーザーは、アプリの再生中にアシスタントに何かを頼むことができます 前のリクエストのコンテンツです。つまり、アプリは新しいインテントを受信して、 再生アクティビティがすでに開始されてアクティブな間に再生を開始する。

ディープリンクを含むインテントをサポートするアクティビティは、オーバーライドする必要があります。 onNewIntent() 新しいリクエストを処理できます

再生を開始すると、アシスタントは フラグ 渡します。特に、 FLAG_ACTIVITY_CLEAR_TOP または FLAG_ACTIVITY_NEW_TASK、または両方。コードが これらのフラグを処理する必要がない場合、Android システムはそれらに応答します。 これは、新しい URI を持つ 2 番目の再生リクエストが到着したときのアプリの動作に影響する可能性があります。 前の URI が再生中です。この場合にアプリがどのように反応するか、テストすることをおすすめします。adb コマンドを使用すると、 状況をシミュレートするには、線ツールを使用します(定数 0x14000000 は、2 つのフラグのブール値のビット演算 OR です)。

adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<first_uri>"' -f 0x14000000
adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<second_uri>"' -f 0x14000000

サービスからの再生

アプリに media browser service アシスタントからの接続を許可する アシスタントは、サービスと通信することにより、 media session。 メディア ブラウザ サービスがアクティビティを起動すべきではありません。 アシスタントは、定義した PendingIntent に基づいてアクティビティを起動します setSessionActivity() で処理します。

リクエストする際には、必ず MediaSession.Token を メディア ブラウザ サービスを初期化します。 サポートされている再生アクションを必ず設定してください 常に同期されません。アシスタントはメディアの通知を受け取ります アプリを使用して、アシスタントが最初の再生を送信する前に再生操作を設定します。 使用できます。

サービスから開始するために、アシスタントはメディア ブラウザ クライアント API を実装します。 このメソッドは TransportControls 呼び出しを実行し、 アプリのメディア セッションを指定します。

次の図は、Google アシスタントによって生成される通話の順序と、 対応するメディア セッション コールバックを呼び出せます。(prepare コールバックは、 確認してください)。すべての呼び出しは非同期です。アシスタントは アプリからの応答を待ちます。

メディア セッションを使用した再生の開始

ユーザーが再生する音声コマンドを発行すると、アシスタントは短い通知を返します。 アナウンスが終わるとすぐに、Google アシスタントから PLAY アクションが発行されます。特定の再生状態を待つことはありません。

アプリが ACTION_PREPARE_* アクションをサポートしている場合、アシスタントは通知を開始する前に PREPARE アクションを呼び出します。

MediaBrowserService への接続

サービスを使用してアプリを起動するには、アシスタントがアプリの MediaBrowserService に接続し、 その MediaSession.Token を取得します接続リクエストは、サービスの onGetRoot() メソッドを呼び出します。リクエストの処理には、次の 2 つの方法があります。

  • すべての接続リクエストを受け入れる場合
  • Google アシスタント アプリからの接続リクエストのみを受け入れる
で確認できます。

すべての接続リクエストを受け入れる場合

Google アシスタントからメディア セッションにコマンドを送信できるようにするには、BrowserRoot を返す必要があります。最も簡単な方法は、すべての MediaBrowser アプリが MediaBrowserService に接続できるようにすることです。それには、null 以外の BrowserRoot を返す必要があります。Universal Music Player の該当部分のコードは次のとおりです。

Kotlin

override fun onGetRoot(
        clientPackageName: String,
        clientUid: Int,
        rootHints: Bundle?
): BrowserRoot? {

    // To ensure you are not allowing any arbitrary app to browse your app's contents, you
    // need to check the origin:
    if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return an empty browser root.
        // If you return null, then the media browser will not be able to connect and
        // no further calls will be made to other media browsing methods.
        Log.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. Returning empty "
                + "browser root so all apps can use MediaController. $clientPackageName")
        return MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null)
    }

    // Return browser roots for browsing...
}

Java

@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid,
                             Bundle rootHints) {

    // To ensure you are not allowing any arbitrary app to browse your app's contents, you
    // need to check the origin:
    if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return an empty browser root.
        // If you return null, then the media browser will not be able to connect and
        // no further calls will be made to other media browsing methods.
        LogHelper.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. "
                + "Returning empty browser root so all apps can use MediaController."
                + clientPackageName);
        return new MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null);
    }

    // Return browser roots for browsing...
}

Google アシスタント アプリのパッケージと署名を受け入れる場合

Google アシスタントからメディア ブラウザ サービスへの接続を明示的に許可するには、パッケージ名と署名を確認します。アプリでは、MediaBrowserService の onGetRoot メソッドでパッケージ名を受け取ります。Google アシスタントからメディア セッションにコマンドを送信できるようにするには、BrowserRoot を返す必要があります。「 ユニバーサル音楽プレーヤー サンプルは、既知のパッケージ名と署名のリストを保持します。以下は、Google アシスタントにより使用されるパッケージ名と署名です。

<signature name="Google" package="com.google.android.googlequicksearchbox">
    <key release="false">19:75:b2:f1:71:77:bc:89:a5:df:f3:1f:9e:64:a6:ca:e2:81:a5:3d:c1:d1:d5:9b:1d:14:7f:e1:c8:2a:fa:00</key>
    <key release="true">f0:fd:6c:5b:41:0f:25:cb:25:c3:b5:33:46:c8:97:2f:ae:30:f8:ee:74:11:df:91:04:80:ad:6b:2d:60:db:83</key>
</signature>

<signature name="Google Assistant on Android Automotive OS" package="com.google.android.carassistant">
    <key release="false">17:E2:81:11:06:2F:97:A8:60:79:7A:83:70:5B:F8:2C:7C:C0:29:35:56:6D:46:22:BC:4E:CF:EE:1B:EB:F8:15</key>
    <key release="true">74:B6:FB:F7:10:E8:D9:0D:44:D3:40:12:58:89:B4:23:06:A6:2C:43:79:D0:E5:A6:62:20:E3:A6:8A:BF:90:E2</key>
</signature>