出力切り替えは、Android 13 以降のコンテンツのローカル再生とリモート再生をシームレスに転送できる Cast SDK の機能です。送信元アプリがコンテンツの再生場所を簡単にすばやく制御できるようにすることが目的です。Output Switcher は MediaRouter
ライブラリを使用して、スマートフォンのスピーカー、ペア設定された Bluetooth デバイス、リモートの Cast 対応デバイス間でコンテンツの再生を切り替えます。ユースケースは次のシナリオに分類できます。
アプリに出力切り替えツールを実装する方法については、CastVideos-android サンプルアプリをダウンロードして使用してください。
このガイドで説明する手順に沿って、ローカルからリモート、リモートからローカル、リモートからリモートをサポートするように出力切り替え機能を有効にする必要があります。ローカル デバイスのスピーカーとペア設定された Bluetooth デバイス間の転送をサポートするために、追加の手順は必要ありません。
出力切り替え UI
出力切り替えツールには、使用可能なローカル デバイスとリモート デバイスのほか、デバイスの選択状況、接続状況、現在の音量レベルなどのデバイスの現在の状態が表示されます。現在のデバイス以外に他のデバイスがある場合は、[他のデバイス] をクリックして、選択したデバイスにメディアの再生を転送できます。
既知の問題
- ローカル再生用に作成されたメディア セッションは、Cast SDK 通知に切り替えると閉じられ、再作成されます。
エントリ ポイント
メディア通知
ローカル再生(ローカル再生)の MediaSession
を使用してアプリがメディア通知を投稿すると、メディア通知の右上隅に、現在コンテンツが再生されているデバイス名(スマートフォンのスピーカーなど)を含む通知チップが表示されます。通知チップをタップすると、出力切り替えダイアログのシステム UI が開きます。
音量の設定
出力切り替えダイアログ システム UI は、デバイスの物理的な音量ボタンをクリックし、下部にある設定アイコンをタップして、[<アプリ名> を <キャスト デバイス> で再生] テキストをタップすることでもトリガーできます。
ステップの概要
- 前提条件が満たされていることを確認する
- AndroidManifest.xml で出力切り替え機能を有効にする
- バックグラウンド キャスト用の SessionManagerListener を更新
- リモート間接リモートのサポートを追加
- setRemoteToLocalEnabled フラグを設定する
- ローカルで再生を続ける
前提条件
- 既存の Android アプリを AndroidX に移行します。
- 出力切り替えツールに必要な最小バージョンの Android Sender SDK を使用するように、アプリの
build.gradle
を更新します。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.Builder()
にメディアに関連するすべての PlaybackStateCompat
定数を指定します。
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 の [Receiver App] 設定で 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); } }
リモート間テスト
この機能をテストするには:
- 従来のキャスト機能またはローカルからリモートを使用して、コンテンツを Cast 対応デバイスにキャストします。
- エントリ ポイントのいずれかを使用して、出力切り替えツールを開きます。
- 別の Cast 対応デバイスをタップすると、オーディオアプリがコンテンツを追加デバイスに拡張し、動的グループを作成します。
- Cast 対応デバイスをもう一度タップすると、そのデバイスは動的グループから削除されます。