出力スイッチャー

出力スイッチャーは、シームレスな転送を可能にする Cast SDK の機能です。 コンテンツのローカル再生とリモート再生の違い(Android 13 以降)目標 送信側アプリでコンテンツの再生先を簡単かつ迅速に制御できるようにすることです。 Output Switcher は MediaRouter ライブラリを使用して、スマートフォンのスピーカー、ペア設定された Bluetooth デバイス、リモートの Cast 対応デバイス間でコンテンツの再生を切り替えます。ユースケースは次のように分類できます。 シナリオ:

CastVideos-android サンプルアプリをダウンロードして使用する をご覧ください。

ローカルからリモート、リモートからローカルをサポートするには、出力スイッチャーを有効にする必要があります リモートからリモートへの移動を 制御することもできます現在、 ローカル デバイス間の転送をサポートするために必要となる追加の手順 ペア設定した Bluetooth デバイスに接続できます。

出力スイッチャー UI

出力スイッチャーには、使用可能なローカル デバイスとリモート デバイスが表示されます。 現在のデバイスの状態(デバイスが選択されているかどうかなど) 現在の音量レベルが表示されます。現在のデバイス以外に他のデバイスがある場合は、[他のデバイス] をクリックして、選択したデバイスにメディアの再生を転送できます。

既知の問題

  • ローカル再生用に作成されたメディア セッションは破棄され、再作成されます 」が表示されます。

エントリ ポイント

メディアに関する通知

アプリから投稿されたメディア通知を MediaSession: ローカル再生(ローカルでの再生中)、メディア通知の右上 通知チップにそのデバイス名(スマートフォンのスピーカーなど)が 。通知チップをタップすると開く [Output Switcher] ダイアログのシステム UI。

音量の設定

出力切り替えダイアログ システム UI は、デバイスの物理的な音量ボタンをクリックし、下部にある設定アイコンをタップして、[<アプリ名> を <キャスト デバイス> で再生] テキストをタップすることでもトリガーできます。

ステップの概要

前提条件

  1. 既存の Android アプリを AndroidX に移行します。
  2. 必要最小限のバージョンのバージョンのものを使用するには、アプリの build.gradle を更新してください 出力スイッチャー用の Android Sender SDK:
    dependencies {
      ...
      implementation 'com.google.android.gms:play-services-cast-framework:21.2.0'
      ...
    }
  3. アプリがメディア通知をサポートしている。
  4. Android 13 を搭載したデバイス。

メディア通知の設定

出力スイッチャーを使用するには audio動画アプリ 再生ステータスを表示するメディア通知を作成し、 ローカル再生のメディア コントロールを設定できます。そのためには、Terraform で MediaSessionMediaStyle MediaSession のトークンを使用し、メディア コントロールを 通知を受け取ります。

現在 MediaStyleMediaSession を使用していない場合は、スニペットは 以下に、設定方法と、メディアの設定に関するガイドを示します。 セッション コールバックを audio動画 アプリ:

KotlinJava
// 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()

KotlinJava
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()
)
</ph>
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()

KotlinJava
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()
)
</ph>
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 で出力スイッチャーを有効にする

出力スイッチャーを有効にするには、 MediaTransferReceiver アプリの AndroidManifest.xml に追加する必要があります。そうでない場合、この機能は 無効になり、remote-to-local 機能フラグも無効になります。

<application>
    ...
    <receiver
         android:name="androidx.mediarouter.media.MediaTransferReceiver"
         android:exported="true">
    </receiver>
    ...
</application>

MediaTransferReceiver システムとデバイス間のメディア転送を可能にするブロードキャスト レシーバです。 UI です。詳しくは、MediaTransferReceiver リファレンス をご覧ください。

ローカルからリモート

再生をローカルからリモートに切り替えると、Cast SDK が起動します 自動的にキャスト セッションが削除されます。ただし、アプリは移行元のプラットフォームから ローカルからリモートへ(たとえば、ローカルの再生を停止する) キャスト デバイスにメディアを読み込みます。アプリは、onSessionStarted() コールバックと onSessionEnded() コールバックを使用して Cast SessionManagerListener をリッスンし、Cast SessionManager コールバックを受信したときにアクションを処理する必要があります。アプリはこれらのコールバックが有効な状態で Output Switcher ダイアログが開いており、アプリがフォアグラウンドではない。

バックグラウンド キャスト用の SessionManagerListener を更新

従来のキャストでは、アプリが がフォアグラウンドにあります。通常のキャスト エクスペリエンスは、ユーザーがキャスト アイコンをクリックすると開始されます。 をタップし、メディアをストリーミングするデバイスを選択します。この場合、アプリは Google Cloud コンソールで SessionManagerListener 場所: onCreate()、または onStart() リスナーの登録を解除し、 onStop() または onDestroy() 追跡します。

出力スイッチャーを使用した新しいキャスト機能により、 バックグラウンドでの キャストが可能です。これは特に、音声や動画などの バックグラウンドでの再生中に通知を送信するアプリ。アプリは登録が可能 SessionManager サービスの onCreate() でリスナーを設定し、onDestroy() で登録を解除する 構成されます。アプリは常にローカルからリモートへのコールバック( onSessionStarted) アプリがバックグラウンドで動作しているとき。

アプリが MediaBrowserService SessionManagerListener を登録することをおすすめします。 できます。

KotlinJava
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)
        }
    }
}
</ph>
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 デバイスをキャスト デバイスに接続する。

リモートからローカルへ

出力スイッチャーは、リモート再生から またはローカルの Bluetooth デバイスに接続します。この設定を有効にするには、 setRemoteToLocalEnabled CastOptionstrueに報告しました。

現在の送信デバイスが複数の送信者を含む既存のセッションに参加していて、アプリが現在のメディアをローカルに転送できるかどうかを確認する必要がある場合は、SessionTransferCallbackonTransferred コールバックを使用して SessionState を確認する必要があります。

setRemoteToLocalEnabled フラグを設定する

CastOptions.Builder スマートフォンのスピーカーとローカルの Bluetooth デバイスを転送先として表示または非表示にする setRemoteToLocalEnabled を提供します。 アクティブなキャスト セッションがある場合に、出力の切り替えダイアログに表示されます。

KotlinJava
class CastOptionsProvider : OptionsProvider {
    fun getCastOptions(context: Context?): CastOptions {
        ...
        return Builder()
            ...
            .setRemoteToLocalEnabled(true)
            .build()
    }
}
</ph>
public class CastOptionsProvider implements OptionsProvider {
    @Override
    public CastOptions getCastOptions(Context context) {
        ...
        return new CastOptions.Builder()
            ...
            .setRemoteToLocalEnabled(true)
            .build()
  }
}

ローカルでの再生を続行

リモートからローカルへの変換をサポートするアプリは、SessionTransferCallback を登録する必要があります。 イベント発生時に通知を受け取れるようにして、メディアがアップロードされる ローカルで転送して再生を続行できる権限があります。

CastContext#addSessionTransferCallback(SessionTransferCallback) アプリがその SessionTransferCallback を登録できるようにします。 また、onTransferredonTransferFailed のコールバックをリッスンするのは ローカル再生に転送します。

アプリが SessionTransferCallback の登録を解除すると、次のようになります。 アプリは SessionTransferCallback を受信しなくなります

SessionTransferCallback は既存の SessionManagerListener コールバックの拡張機能であり、onSessionEnded がトリガーされた後にトリガーされます。データの リモートからローカルへのコールバックは次のようになります。

  1. onTransferring
  2. onSessionEnding
  3. onSessionEnded
  4. onTransferred

出力スイッチャーは、 アプリがバックグラウンドで動作していてキャストしている場合、アプリがローカルへの転送を処理する必要がある バックグラウンド再生に対応しているかどうかによって異なります。ケース 失敗した転送、onTransferFailed エラーが発生するたびに発生します。

バックグラウンド再生をサポートするアプリ

バックグラウンドでの再生をサポートするアプリ(主にオーディオ アプリ)の場合: Service を使用することをおすすめします(例: MediaBrowserService)。サービス 「onTransferred」を聞く アプリがフォアグラウンドで動作しているときや、アプリがフォアグラウンドで動作しているとき、 説明します。

KotlinJava
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.
        }
    }
}
</ph>
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 から必要な情報(メディアのメタデータや再生位置など)を保存する必要があります。アプリがバックグラウンドからフォアグラウンドに移行された場合、ローカル再生は保存された情報で続行する必要があります。

KotlinJava
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.
        }
    }
}
</ph>
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 for Audio に対応しているアプリのことです。 Google Cast SDK Developer の コンソール

スピーカーによるストリーミング拡張

出力スイッチャーを使用するオーディオ アプリでは、音声を拡大できます。 [ストリーミング] を使用したキャスト セッション中に複数の Cast 対応スピーカー デバイスに接続 拡張。

この機能は Cast プラットフォームでサポートされているため、これ以上の対応は必要ありません アプリがデフォルト UI を使用している場合、変更されます。カスタム UI を使用すると、 アプリがグループにキャストしていることを反映するように UI を更新します。

ストリーム拡張中に新しい拡張グループ名を取得するには、次の操作を行います。 登録して Cast.Listener 使用 CastSession#addCastListener。 次に呼び出します CastSession#getCastDevice() onDeviceNameChanged コールバック中に発生します。

KotlinJava
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)
    }
}
</ph>
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);
    }
}

リモート間のテスト

機能をテストするには:

  1. Cast 対応デバイスにコンテンツをキャストするには、通常のキャストを使用するか、 local-to-remote
  2. いずれかのエントリ ポイントを使用して出力スイッチャーを開きます。
  3. 別の Cast 対応デバイスをタップすると、オーディオ アプリのコンテンツが 動的グループを作成します
  4. Cast 対応デバイスをもう一度タップすると、デバイスがダイナミックから削除されます できます。