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() |
準備 API を実装すると、音声コマンド後の再生レイテンシが 減らすことができます再生レイテンシを改善したいメディアアプリは、 コンテンツのキャッシュ保存とメディア再生の準備を開始する時間が長くなります。
検索クエリの解析
ユーザーが特定のメディア アイテムを検索したとき(例: 「ジャズを再生して」
[アプリ名]」や「[曲のタイトル] を聴く」、
onPrepareFromSearch()
または
onPlayFromSearch()
コールバック メソッドがクエリ パラメータとエクストラ バンドルを受け取ります。
アプリは音声検索のクエリを解析し、次の手順で再生を開始する必要があります。 手順:
- 音声検索から返されたエクストラ バンドルと検索語句の文字列を使用 結果をフィルタできます
- その結果に基づいて再生キューを作成する。
- 結果の中から最も関連性の高いメディア アイテムを再生する。
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) |
字幕オン / オフ |
注:
- 移動コマンドを機能させるには、
PlaybackState
のstate, 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
をインテントに追加します。
アシスタントが、値が true
の EXTRA_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>