出力切り替えは、Android 13 以降でコンテンツのローカル再生とリモート再生をシームレスに切り替えられる Cast SDK の機能です。この目標は、送信側アプリがコンテンツの再生場所を簡単かつ迅速に制御できるようにすることです。出力スイッチャーは MediaRouter
ライブラリを使用して、コンテンツの再生をスマートフォンのスピーカー、ペア設定された Bluetooth デバイス、リモートの Cast 対応デバイスの間で切り替えます。ユースケースは、次のシナリオに分類できます。
アプリで出力スイッチャーを実装する方法については、CastVideos-android サンプルアプリをダウンロードして使用してください。
このガイドで説明する手順を使用して、ローカルからリモート、リモートからローカル、リモートからリモートをサポートするために、出力切り替えを有効にする必要があります。ローカル デバイスのスピーカーとペア設定された Bluetooth デバイス間の転送をサポートするために必要な追加の手順はありません。
出力スイッチャーの UI
出力スイッチャーには、利用可能なローカル デバイスとリモート デバイス、およびデバイスが選択されているかどうか、接続中かどうか、現在の音量レベルなどの現在のデバイスの状態が表示されます。現在のデバイス以外にデバイスがある場合、別のデバイスをクリックすると、選択したデバイスにメディアの再生を転送できます。
既知の問題
- ローカル再生用に作成されたメディア セッションは、Cast SDK 通知に切り替えるときに破棄され、再作成されます。
エントリ ポイント
メディア通知
アプリがローカル再生(ローカルで再生)用に MediaSession
を含むメディア通知を投稿すると、メディア通知の右上隅に、コンテンツが現在再生されているデバイス名(電話スピーカーなど)を含む通知チップが表示されます。通知チップをタップすると、出力スイッチャー ダイアログのシステム UI が開きます。
音量の設定
出力切り替えダイアログのシステム UI は、デバイスの物理的な音量ボタンをクリックする、下部の設定アイコンをタップする、[<キャスト デバイス> で <アプリ名> を再生] のテキストをタップすることでもトリガーできます。
ステップの概要
- 前提条件が満たされていることを確認する
- AndroidManifest.xml で出力スイッチャーを有効にする
- バックグラウンド キャスト用に SessionManagerListener を更新
- リモート間サポートの追加
- setRemoteToLocalEnabled フラグを設定する
- ローカルで再生を続行する
前提条件
- 既存の Android アプリを AndroidX に移行します。
- アプリの
build.gradle
を更新して、出力スイッチャーに必要な最小バージョンの Android Sender SDK を使用します。dependencies { ... implementation 'com.google.android.gms:play-services-cast-framework:21.2.0' ... }
- アプリはメディア通知をサポートします。
- Android 13 を搭載したデバイス。
メディアの通知を設定する
出力スイッチャーを使用するには、音声アプリと動画アプリが、ローカル再生用のメディアの再生ステータスとコントロールを表示するメディア通知を作成する必要があります。これを行うには、MediaSession
を作成し、MediaSession
のトークンで MediaStyle
を設定し、通知にメディア コントロールを設定する必要があります。
現在 MediaStyle
と MediaSession
を使用していない場合は、次のスニペットで設定方法を確認してください。また、音声アプリと動画アプリのメディア セッション コールバックの設定に関するガイドもご覧ください。
// Create a media session. NotificationCompat.MediaStyle // PlayerService is your own Service or Activity responsible for media playback. val mediaSession = MediaSessionCompat(this, "PlayerService") // Create a MediaStyle object and supply your media session token to it. val mediaStyle = Notification.MediaStyle().setMediaSession(mediaSession.sessionToken) // Create a Notification which is styled by your MediaStyle object. // This connects your media session to the media controls. // Don't forget to include a small icon. val notification = Notification.Builder(this@PlayerService, CHANNEL_ID) .setStyle(mediaStyle) .setSmallIcon(R.drawable.ic_app_logo) .build() // Specify any actions which your users can perform, such as pausing and skipping to the next track. val pauseAction: Notification.Action = Notification.Action.Builder( pauseIcon, "Pause", pauseIntent ).build() notification.addAction(pauseAction)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { // Create a media session. NotificationCompat.MediaStyle // PlayerService is your own Service or Activity responsible for media playback. MediaSession mediaSession = new MediaSession(this, "PlayerService"); // Create a MediaStyle object and supply your media session token to it. Notification.MediaStyle mediaStyle = new Notification.MediaStyle().setMediaSession(mediaSession.getSessionToken()); // Specify any actions which your users can perform, such as pausing and skipping to the next track. Notification.Action pauseAction = Notification.Action.Builder(pauseIcon, "Pause", pauseIntent).build(); // Create a Notification which is styled by your MediaStyle object. // This connects your media session to the media controls. // Don't forget to include a small icon. String CHANNEL_ID = "CHANNEL_ID"; Notification notification = new Notification.Builder(this, CHANNEL_ID) .setStyle(mediaStyle) .setSmallIcon(R.drawable.ic_app_logo) .addAction(pauseAction) .build(); }
また、メディアの情報を通知に設定するには、メディアのメタデータと再生状態を MediaSession
に追加する必要があります。
MediaSession
にメタデータを追加するには、setMetaData()
を使用し、メディアに関連するすべての MediaMetadata
定数を MediaMetadataCompat.Builder()
で指定します。
mediaSession.setMetadata(MediaMetadataCompat.Builder() // Title .putString(MediaMetadata.METADATA_KEY_TITLE, currentTrack.title) // Artist // Could also be the channel name or TV series. .putString(MediaMetadata.METADATA_KEY_ARTIST, currentTrack.artist) // Album art // Could also be a screenshot or hero image for video content // The URI scheme needs to be "content", "file", or "android.resource". .putString( MediaMetadata.METADATA_KEY_ALBUM_ART_URI, currentTrack.albumArtUri) ) // Duration // If duration isn't set, such as for live broadcasts, then the progress // indicator won't be shown on the seekbar. .putLong(MediaMetadata.METADATA_KEY_DURATION, currentTrack.duration) .build() )
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { mediaSession.setMetadata( new MediaMetadataCompat.Builder() // Title .putString(MediaMetadata.METADATA_KEY_TITLE, currentTrack.title) // Artist // Could also be the channel name or TV series. .putString(MediaMetadata.METADATA_KEY_ARTIST, currentTrack.artist) // Album art // Could also be a screenshot or hero image for video content // The URI scheme needs to be "content", "file", or "android.resource". .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, currentTrack.albumArtUri) // Duration // If duration isn't set, such as for live broadcasts, then the progress // indicator won't be shown on the seekbar. .putLong(MediaMetadata.METADATA_KEY_DURATION, currentTrack.duration) .build() ); }
再生状態を MediaSession
に追加するには、setPlaybackState()
を使用し、メディアに関連するすべての PlaybackStateCompat
定数を PlaybackStateCompat.Builder()
に指定します。
mediaSession.setPlaybackState( PlaybackStateCompat.Builder() .setState( PlaybackStateCompat.STATE_PLAYING, // Playback position // Used to update the elapsed time and the progress bar. mediaPlayer.currentPosition.toLong(), // Playback speed // Determines the rate at which the elapsed time changes. playbackSpeed ) // isSeekable // Adding the SEEK_TO action indicates that seeking is supported // and makes the seekbar position marker draggable. If this is not // supplied seek will be disabled but progress will still be shown. .setActions(PlaybackStateCompat.ACTION_SEEK_TO) .build() )
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { mediaSession.setPlaybackState( new PlaybackStateCompat.Builder() .setState( PlaybackStateCompat.STATE_PLAYING, // Playback position // Used to update the elapsed time and the progress bar. mediaPlayer.currentPosition.toLong(), // Playback speed // Determines the rate at which the elapsed time changes. playbackSpeed ) // isSeekable // Adding the SEEK_TO action indicates that seeking is supported // and makes the seekbar position marker draggable. If this is not // supplied seek will be disabled but progress will still be shown. .setActions(PlaybackStateCompat.ACTION_SEEK_TO) .build() ); }
動画アプリの通知の動作
バックグラウンドでのローカル再生をサポートしていない動画アプリまたは音声アプリは、再生がサポートされていない状況でメディア コマンドを送信する際の問題を回避するために、メディア通知の特定の動作を備えている必要があります。
- メディアをローカルで再生し、アプリがフォアグラウンドにあるときにメディア通知を投稿します。
- アプリがバックグラウンドにある場合、ローカル再生を一時停止して通知を非表示にします。
- アプリがフォアグラウンドに戻ると、ローカル再生が再開され、通知が再投稿されるはずです。
AndroidManifest.xml で出力スイッチャーを有効にする
出力の切り替えを有効にするには、アプリの AndroidManifest.xml
に MediaTransferReceiver
を追加する必要があります。有効になっていない場合、この機能は有効にならず、リモートからローカルへの機能フラグも無効になります。
<application>
...
<receiver
android:name="androidx.mediarouter.media.MediaTransferReceiver"
android:exported="true">
</receiver>
...
</application>
MediaTransferReceiver
は、システム UI を備えたデバイス間でメディア転送を可能にするブロードキャスト レシーバです。詳しくは、MediaTransferReceiver リファレンスをご覧ください。
ローカルからリモート
ユーザーが再生をローカルからリモートに切り替えると、Cast SDK は自動的にキャスト セッションを開始します。ただし、アプリはローカルからリモートへの切り替えを処理する必要があります(ローカル再生を停止して、キャスト デバイスにメディアを読み込むなど)。アプリは、onSessionStarted()
コールバックと onSessionEnded()
コールバックを使用して、Cast SessionManagerListener
をリッスンし、Cast SessionManager
コールバックを受信したときにアクションを処理する必要があります。アプリは、出力スイッチャー ダイアログが開いてアプリがフォアグラウンドにない場合でも、これらのコールバックが有効であることを確認する必要があります。
バックグラウンド キャスト用に SessionManagerListener を更新
アプリがフォアグラウンドにある場合、以前の Cast エクスペリエンスはローカルからリモートへの切り替えをすでにサポートしています。一般的なキャスト エクスペリエンスは、ユーザーがアプリのキャスト アイコンをクリックして、メディアをストリーミングするデバイスを選択すると始まります。この場合、アプリは onCreate()
または onStart()
で SessionManagerListener
に登録し、アプリのアクティビティの onStop()
または onDestroy()
でリスナーの登録を解除する必要があります。
出力スイッチャーを使用した新しいキャスト機能では、アプリがバックグラウンドにあるときにキャストを開始できます。これは、バックグラウンドで再生中に通知を投稿するオーディオ アプリで特に便利です。アプリは、サービスの onCreate()
で SessionManager
リスナーを登録し、サービスの onDestroy()
で登録を解除できます。アプリがバックグラウンドにある場合、アプリは常にローカルからリモートへのコールバック(onSessionStarted
など)を受信する必要があります。
アプリが MediaBrowserService
を使用している場合は、そこに SessionManagerListener
を登録することをおすすめします。
class MyService : Service() { private var castContext: CastContext? = null protected fun onCreate() { castContext = CastContext.getSharedInstance(this) castContext .getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession::class.java) } protected fun onDestroy() { if (castContext != null) { castContext .getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession::class.java) } } }
public class MyService extends Service { private CastContext castContext; @Override protected void onCreate() { castContext = CastContext.getSharedInstance(this); castContext .getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession.class); } @Override protected void onDestroy() { if (castContext != null) { castContext .getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession.class); } } }
このアップデートにより、アプリがバックグラウンドにある場合、ローカルからリモートへのキャストは従来のキャストと同じように動作し、Bluetooth デバイスから Cast デバイスへの切り替えに追加の作業は必要ありません。
リモートからローカル
出力スイッチャーを使用すると、リモート再生からスマートフォンのスピーカーまたはローカル Bluetooth デバイスに切り替えることができます。これを有効にするには、CastOptions
で setRemoteToLocalEnabled
フラグを true
に設定します。
現在の送信側デバイスが複数の送信側が存在する既存のセッションに参加し、アプリが現在のメディアをローカルで転送できるかどうかを確認する必要がある場合、アプリは SessionTransferCallback
の onTransferred
コールバックを使用して SessionState
を確認する必要があります。
setRemoteToLocalEnabled フラグを設定する
CastOptions.Builder
は、アクティブなキャスト セッションがある場合に、出力スイッチャー ダイアログで転送先としてスマートフォンのスピーカーとローカル Bluetooth デバイスを表示または非表示にする setRemoteToLocalEnabled
を提供します。
class CastOptionsProvider : OptionsProvider { fun getCastOptions(context: Context?): CastOptions { ... return Builder() ... .setRemoteToLocalEnabled(true) .build() } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { ... return new CastOptions.Builder() ... .setRemoteToLocalEnabled(true) .build() } }
ローカルで再生を続行する
リモートからローカルへの切り替えをサポートするアプリは、イベント発生時に通知を受け取って、メディアの転送とローカルでの再生の継続を許可するかどうかを確認できるように、SessionTransferCallback
を登録する必要があります。
CastContext#addSessionTransferCallback(SessionTransferCallback)
を使用すると、アプリは SessionTransferCallback
を登録し、送信者がローカル再生に転送されたときに onTransferred
と onTransferFailed
のコールバックをリッスンできます。
アプリが SessionTransferCallback
の登録を解除すると、アプリは SessionTransferCallback
を受信しなくなります。
SessionTransferCallback
は既存の SessionManagerListener
コールバックの拡張機能で、onSessionEnded
がトリガーされた後にトリガーされます。リモートからローカルへのコールバックの順序は次のとおりです。
onTransferring
onSessionEnding
onSessionEnded
onTransferred
アプリがバックグラウンドでキャストしている場合、メディア通知チップから出力切り替えツールを開くことができるため、アプリはバックグラウンド再生をサポートしているかどうかに応じて、ローカルへの転送を異なる方法で処理する必要があります。移行が失敗した場合、エラーが発生するたびに onTransferFailed
が発生します。
バックグラウンド再生をサポートするアプリ
バックグラウンドでの再生をサポートするアプリ(通常はオーディオ アプリ)では、Service
(MediaBrowserService
など)を使用することが推奨されます。サービスは onTransferred
コールバックをリッスンし、アプリがフォアグラウンドまたはバックグラウンドにあるときにローカルで再生を再開する必要があります。
class MyService : Service() { private var castContext: CastContext? = null private var sessionTransferCallback: SessionTransferCallback? = null protected fun onCreate() { castContext = CastContext.getSharedInstance(this) castContext.getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession::class.java) sessionTransferCallback = MySessionTransferCallback() castContext.addSessionTransferCallback(sessionTransferCallback) } protected fun onDestroy() { if (castContext != null) { castContext.getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession::class.java) if (sessionTransferCallback != null) { castContext.removeSessionTransferCallback(sessionTransferCallback) } } } class MySessionTransferCallback : SessionTransferCallback() { fun onTransferring(@SessionTransferCallback.TransferType transferType: Int) { // Perform necessary steps prior to onTransferred } fun onTransferred(@SessionTransferCallback.TransferType transferType: Int, sessionState: SessionState?) { if (transferType == SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) { // Remote stream is transferred to the local device. // Retrieve information from the SessionState to continue playback on the local player. } } fun onTransferFailed(@SessionTransferCallback.TransferType transferType: Int, @SessionTransferCallback.TransferFailedReason transferFailedReason: Int) { // Handle transfer failure. } } }
public class MyService extends Service { private CastContext castContext; private SessionTransferCallback sessionTransferCallback; @Override protected void onCreate() { castContext = CastContext.getSharedInstance(this); castContext.getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession.class); sessionTransferCallback = new MySessionTransferCallback(); castContext.addSessionTransferCallback(sessionTransferCallback); } @Override protected void onDestroy() { if (castContext != null) { castContext.getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession.class); if (sessionTransferCallback != null) { castContext.removeSessionTransferCallback(sessionTransferCallback); } } } public static class MySessionTransferCallback extends SessionTransferCallback { public MySessionTransferCallback() {} @Override public void onTransferring(@SessionTransferCallback.TransferType int transferType) { // Perform necessary steps prior to onTransferred } @Override public void onTransferred(@SessionTransferCallback.TransferType int transferType, SessionState sessionState) { if (transferType==SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) { // Remote stream is transferred to the local device. // Retrieve information from the SessionState to continue playback on the local player. } } @Override public void onTransferFailed(@SessionTransferCallback.TransferType int transferType, @SessionTransferCallback.TransferFailedReason int transferFailedReason) { // Handle transfer failure. } } }
バックグラウンド再生に対応していないアプリ
バックグラウンド再生をサポートしていないアプリ(通常は動画アプリ)では、onTransferred
コールバックをリッスンし、アプリがフォアグラウンドにある場合はローカルで再生を再開することが推奨されます。
アプリがバックグラウンドにある場合は、再生を一時停止し、SessionState
から必要な情報(メディア メタデータや再生位置など)を保存する必要があります。アプリがバックグラウンドからフォアグラウンドに切り替わった場合、ローカル再生は保存された情報で継続されるべきです。
class MyActivity : AppCompatActivity() { private var castContext: CastContext? = null private var sessionTransferCallback: SessionTransferCallback? = null protected fun onCreate() { castContext = CastContext.getSharedInstance(this) castContext.getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession::class.java) sessionTransferCallback = MySessionTransferCallback() castContext.addSessionTransferCallback(sessionTransferCallback) } protected fun onDestroy() { if (castContext != null) { castContext.getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession::class.java) if (sessionTransferCallback != null) { castContext.removeSessionTransferCallback(sessionTransferCallback) } } } class MySessionTransferCallback : SessionTransferCallback() { fun onTransferring(@SessionTransferCallback.TransferType transferType: Int) { // Perform necessary steps prior to onTransferred } fun onTransferred(@SessionTransferCallback.TransferType transferType: Int, sessionState: SessionState?) { if (transferType == SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) { // Remote stream is transferred to the local device. // Retrieve information from the SessionState to continue playback on the local player. } } fun onTransferFailed(@SessionTransferCallback.TransferType transferType: Int, @SessionTransferCallback.TransferFailedReason transferFailedReason: Int) { // Handle transfer failure. } } }
public class MyActivity extends AppCompatActivity { private CastContext castContext; private SessionTransferCallback sessionTransferCallback; @Override protected void onCreate() { castContext = CastContext.getSharedInstance(this); castContext .getSessionManager() .addSessionManagerListener(sessionManagerListener, CastSession.class); sessionTransferCallback = new MySessionTransferCallback(); castContext.addSessionTransferCallback(sessionTransferCallback); } @Override protected void onDestroy() { if (castContext != null) { castContext .getSessionManager() .removeSessionManagerListener(sessionManagerListener, CastSession.class); if (sessionTransferCallback != null) { castContext.removeSessionTransferCallback(sessionTransferCallback); } } } public static class MySessionTransferCallback extends SessionTransferCallback { public MySessionTransferCallback() {} @Override public void onTransferring(@SessionTransferCallback.TransferType int transferType) { // Perform necessary steps prior to onTransferred } @Override public void onTransferred(@SessionTransferCallback.TransferType int transferType, SessionState sessionState) { if (transferType==SessionTransferCallback.TRANSFER_TYPE_FROM_REMOTE_TO_LOCAL) { // Remote stream is transferred to the local device. // Retrieve information from the SessionState to continue playback on the local player. } } @Override public void onTransferFailed(@SessionTransferCallback.TransferType int transferType, @SessionTransferCallback.TransferFailedReason int transferFailedReason) { // Handle transfer failure. } } }
リモートからリモート
出力の切り替えは、ストリーミング拡張を使用して、複数の Cast 対応スピーカー デバイスに拡張する機能をサポートしています。
オーディオ アプリとは、Google Cast SDK Developer Console のレシーバー アプリの設定で Google Cast for Audio をサポートするアプリのことです。
スピーカーによるストリーミング拡張
出力スイッチャーを使用するオーディオ アプリは、ストリーム拡張を使用して、キャスト セッション中に音声を複数の Cast 対応スピーカー デバイスに拡張できます。
この機能は Cast プラットフォームでサポートされており、アプリがデフォルトの UI を使用している場合は、これ以上の変更は必要ありません。カスタム UI を使用している場合、アプリは UI を更新して、アプリがグループにキャストしていることを反映する必要があります。
ストリームの展開中に新しい展開グループ名を取得するには、CastSession#addCastListener
を使用して Cast.Listener
を登録します。次に、onDeviceNameChanged
コールバック中に CastSession#getCastDevice()
を呼び出します。
class MyActivity : Activity() { private var mCastSession: CastSession? = null private lateinit var mCastContext: CastContext private lateinit var mSessionManager: SessionManager private val mSessionManagerListener: SessionManagerListener<CastSession> = SessionManagerListenerImpl() private val mCastListener = CastListener() private inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> { override fun onSessionStarting(session: CastSession?) {} override fun onSessionStarted(session: CastSession?, sessionId: String) { addCastListener(session) } override fun onSessionStartFailed(session: CastSession?, error: Int) {} override fun onSessionSuspended(session: CastSession?, reason Int) { removeCastListener() } override fun onSessionResuming(session: CastSession?, sessionId: String) {} override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) { addCastListener(session) } override fun onSessionResumeFailed(session: CastSession?, error: Int) {} override fun onSessionEnding(session: CastSession?) {} override fun onSessionEnded(session: CastSession?, error: Int) { removeCastListener() } } private inner class CastListener : Cast.Listener() { override fun onDeviceNameChanged() { mCastSession?.let { val castDevice = it.castDevice val deviceName = castDevice.friendlyName // Update UIs with the new cast device name. } } } private fun addCastListener(castSession: CastSession) { mCastSession = castSession mCastSession?.addCastListener(mCastListener) } private fun removeCastListener() { mCastSession?.removeCastListener(mCastListener) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mCastContext = CastContext.getSharedInstance(this) mSessionManager = mCastContext.sessionManager mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java) } override fun onDestroy() { super.onDestroy() mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java) } }
public class MyActivity extends Activity { private CastContext mCastContext; private CastSession mCastSession; private SessionManager mSessionManager; private SessionManagerListener<CastSession> mSessionManagerListener = new SessionManagerListenerImpl(); private Cast.Listener mCastListener = new CastListener(); private class SessionManagerListenerImpl implements SessionManagerListener<CastSession> { @Override public void onSessionStarting(CastSession session) {} @Override public void onSessionStarted(CastSession session, String sessionId) { addCastListener(session); } @Override public void onSessionStartFailed(CastSession session, int error) {} @Override public void onSessionSuspended(CastSession session, int reason) { removeCastListener(); } @Override public void onSessionResuming(CastSession session, String sessionId) {} @Override public void onSessionResumed(CastSession session, boolean wasSuspended) { addCastListener(session); } @Override public void onSessionResumeFailed(CastSession session, int error) {} @Override public void onSessionEnding(CastSession session) {} @Override public void onSessionEnded(CastSession session, int error) { removeCastListener(); } } private class CastListener extends Cast.Listener { @Override public void onDeviceNameChanged() { if (mCastSession == null) { return; } CastDevice castDevice = mCastSession.getCastDevice(); String deviceName = castDevice.getFriendlyName(); // Update UIs with the new cast device name. } } private void addCastListener(CastSession castSession) { mCastSession = castSession; mCastSession.addCastListener(mCastListener); } private void removeCastListener() { if (mCastSession != null) { mCastSession.removeCastListener(mCastListener); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCastContext = CastContext.getSharedInstance(this); mSessionManager = mCastContext.getSessionManager(); mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class); } @Override protected void onDestroy() { super.onDestroy(); mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class); } }
リモート間テスト
この機能をテストするには:
- 従来のキャストまたは local-to-remote を使用して、Cast 対応デバイスにコンテンツをキャストします。
- エントリ ポイントのいずれかを使用して出力スイッチャーを開きます。
- 別の Cast 対応デバイスをタップすると、オーディオ アプリがコンテンツをそのデバイスに拡張し、動的グループが作成されます。
- Cast 対応デバイスをもう一度タップすると、動的グループから削除されます。