Pengalih Output

Pengalih Output adalah fitur SDK Cast yang memungkinkan transfer lancar antara pemutaran konten lokal dan jarak jauh yang dimulai dengan Android 13. Tujuan adalah untuk membantu aplikasi pengirim mengontrol tempat konten diputar dengan mudah dan cepat. Pengalih Output menggunakan MediaRouter library ke mengalihkan pemutaran konten di antara speaker ponsel, perangkat Bluetooth yang disambungkan, dan perangkat yang kompatibel untuk Cast jarak jauh. Kasus penggunaan dapat dibagi menjadi skenario:

Download dan gunakan aplikasi contoh CastVideo-android untuk referensi tentang cara menerapkan Pengalih Output di aplikasi Anda.

Pengalih Output harus diaktifkan untuk mendukung lokal-ke-jarak jauh, jarak jauh-ke-lokal dan jarak jauh ke jarak jauh menggunakan langkah-langkah yang dibahas dalam panduan ini. Tidak ada langkah tambahan yang diperlukan untuk mendukung transfer antarperangkat lokal speaker dan perangkat Bluetooth yang disambungkan.

UI Pengalih Output

Pengalih Output menampilkan perangkat lokal dan jarak jauh yang tersedia serta status perangkat saat ini, termasuk jika perangkat dipilih, terhubung, level volume saat ini. Jika ada perangkat lain selain ke perangkat saat ini, mengklik perangkat lain memungkinkan Anda mentransfer media ke perangkat yang dipilih.

Masalah umum

  • Sesi Media yang dibuat untuk pemutaran lokal akan ditutup dan dibuat ulang saat beralih ke notifikasi SDK Cast.

Titik entri

Notifikasi media

Jika aplikasi memposting notifikasi media dengan MediaSession untuk pemutaran lokal (diputar secara lokal), sudut kanan atas notifikasi media akan menampilkan chip notifikasi dengan nama perangkat (seperti speaker ponsel) yang saat ini digunakan untuk memutar konten. Mengetuk chip notifikasi akan terbuka UI sistem dialog Pengalih Output.

Setelan volume

UI sistem dialog Pengalih Output juga dapat dipicu dengan mengklik tombol volume fisik di perangkat, mengetuk ikon pengaturan di bagian bawah, dan mengetuk "Play <App Name> di <Perangkat Cast>" teks.

Ringkasan langkah-langkah

Prasyarat

  1. Migrasikan aplikasi Android yang ada ke AndroidX.
  2. Update build.gradle aplikasi Anda untuk menggunakan versi minimum yang diperlukan Android Sender SDK untuk Pengalih Output:
    dependencies {
      ...
      implementation 'com.google.android.gms:play-services-cast-framework:21.2.0'
      ...
    }
  3. Aplikasi mendukung notifikasi media.
  4. Perangkat yang menjalankan Android 13.

Menyiapkan Notifikasi Media

Untuk menggunakan Pengalih Output, audio dan Aplikasi video diperlukan untuk membuat notifikasi media guna menampilkan status pemutaran dan untuk media mereka untuk pemutaran lokal. Hal ini memerlukan pembuatan MediaSession, menyetel MediaStyle dengan token MediaSession, dan menyetel kontrol media pada notifikasi.

Jika saat ini Anda tidak menggunakan MediaStyle dan MediaSession, cuplikan di bawah ini menunjukkan cara menyiapkannya. Panduan juga tersedia untuk menyiapkan media callback sesi untuk audio dan video aplikasi:

Kotlin
// 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)
Java
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();
}

Selain itu, untuk mengisi notifikasi dengan informasi untuk media, Anda harus menambahkan metadata dan status pemutaran media ke MediaSession.

Untuk menambahkan metadata ke MediaSession, gunakan setMetaData() dan menyediakan semua informasi yang relevan Konstanta MediaMetadata untuk media Anda dalam MediaMetadataCompat.Builder().

Kotlin
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()
)
Java
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()
    );
}

Untuk menambahkan status pemutaran ke MediaSession, gunakan setPlaybackState() dan berikan semua konstanta PlaybackStateCompat yang relevan untuk media Anda di PlaybackStateCompat.Builder().

Kotlin
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()
)
Java
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()
    );
}

Perilaku notifikasi aplikasi video

Aplikasi video atau aplikasi audio yang tidak mendukung pemutaran lokal di latar belakang harus memiliki perilaku tertentu untuk notifikasi media untuk menghindari masalah dengan mengirimkan perintah media dalam situasi pemutaran tidak didukung:

  • Posting notifikasi media saat memutar media secara lokal dan aplikasi dalam latar depan.
  • Jeda pemutaran lokal dan tutup notifikasi saat aplikasi berada di latar belakang.
  • Saat aplikasi kembali ke latar depan, pemutaran lokal akan dilanjutkan dan pemberitahuan harus diposting ulang.

Mengaktifkan Pengalih Output di AndroidManifest.xml

Untuk mengaktifkan Pengalih Output, MediaTransferReceiver harus ditambahkan ke AndroidManifest.xml aplikasi. Jika tidak, fitur tersebut tidak akan diaktifkan dan tanda fitur jarak jauh ke lokal juga tidak akan valid.

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

Tujuan MediaTransferReceiver adalah penerima siaran yang memungkinkan transfer media antar perangkat dengan UI. Lihat referensi MediaTransferReceiver untuk mengetahui informasi selengkapnya.

Lokal ke jarak jauh

Saat pengguna mengalihkan pemutaran dari lokal ke jarak jauh, Cast SDK akan dimulai sesi Transmisi secara otomatis. Namun, aplikasi perlu menangani peralihan dari lokal ke jarak jauh, misalnya menghentikan pemutaran lokal dan memuat media di perangkat Cast. Aplikasi akan mendengarkan Transmisi SessionManagerListener, menggunakan onSessionStarted() dan onSessionEnded() callback, dan menangani tindakan saat menerima Transmisi SessionManager callback. Aplikasi harus memastikan bahwa callback ini masih aktif saat dialog Pengalih Output dibuka dan aplikasi tidak berada di latar depan.

Memperbarui SessionManagerListener untuk transmisi latar belakang

Pengalaman Transmisi lama sudah mendukung lokal ke jarak jauh saat aplikasi berada di latar depan. Pengalaman Cast yang biasa dimulai saat pengguna mengklik ikon Cast di aplikasi dan pilih perangkat untuk streaming media. Dalam hal ini, aplikasi harus mendaftar ke SessionManagerListener, dalam onCreate() atau onStart() dan membatalkan pendaftaran pemroses onStop() atau onDestroy() aktivitas aplikasi.

Dengan pengalaman baru transmisi menggunakan Pengalih Output, aplikasi dapat memulai melakukan transmisi saat berada di latar belakang. Hal ini sangat berguna untuk audio aplikasi yang memposting notifikasi saat diputar di latar belakang. Aplikasi dapat mendaftar SessionManager pemroses di onCreate() layanan dan membatalkan pendaftaran di onDestroy() dari layanan. Aplikasi harus selalu menerima callback lokal ke jarak jauh (seperti onSessionStarted) saat aplikasi berada di latar belakang.

Jika aplikasi menggunakan MediaBrowserService, sebaiknya daftarkan SessionManagerListener di sana.

Kotlin
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)
        }
    }
}
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);
    }
  }
}

Dengan pembaruan ini, transmisi lokal-ke-jarak jauh bertindak sama seperti transmisi konvensional saat aplikasi ada di latar belakang dan tidak perlu pekerjaan tambahan untuk beralih dari Perangkat Bluetooth ke perangkat Transmisi.

Jarak jauh ke lokal

Pengalih Output menyediakan kemampuan untuk mentransfer dari pemutaran jarak jauh ke speaker telepon atau perangkat Bluetooth lokal. Ini dapat diaktifkan dengan menyetel setRemoteToLocalEnabled flag ke true pada CastOptions.

Untuk kasus ketika perangkat pengirim saat ini bergabung ke sesi yang ada dengan beberapa pengirim dan aplikasi harus memeriksa apakah media saat ini diizinkan untuk ditransfer secara lokal, aplikasi harus menggunakan onTransferred callback dari SessionTransferCallback untuk memeriksa SessionState.

Menyetel tanda setRemoteToLocalEnabled

CastOptions.Builder menyediakan setRemoteToLocalEnabled untuk menampilkan atau menyembunyikan speaker ponsel dan perangkat Bluetooth lokal sebagai target dalam dialog Pengalih Output saat ada sesi Transmisi yang aktif.

Kotlin
class CastOptionsProvider : OptionsProvider {
    fun getCastOptions(context: Context?): CastOptions {
        ...
        return Builder()
            ...
            .setRemoteToLocalEnabled(true)
            .build()
    }
}
Java
public class CastOptionsProvider implements OptionsProvider {
    @Override
    public CastOptions getCastOptions(Context context) {
        ...
        return new CastOptions.Builder()
            ...
            .setRemoteToLocalEnabled(true)
            .build()
  }
}

Melanjutkan pemutaran secara lokal

Aplikasi yang mendukung remote-to-local harus mendaftarkan SessionTransferCallback untuk mendapatkan pemberitahuan saat peristiwa terjadi sehingga mereka dapat memeriksa apakah media harus diizinkan untuk mentransfer dan melanjutkan pemutaran secara lokal.

CastContext#addSessionTransferCallback(SessionTransferCallback) mengizinkan aplikasi mendaftarkan SessionTransferCallback-nya dan memproses callback onTransferred dan onTransferFailed saat pengirim ditransfer ke pemutaran lokal.

Setelah aplikasi membatalkan pendaftaran SessionTransferCallback, aplikasi tidak akan lagi menerima SessionTransferCallback.

SessionTransferCallback merupakan perluasan dari SessionManagerListener yang ada dan dipicu setelah onSessionEnded dipicu. Urutan dari callback jarak jauh ke lokal adalah:

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

Karena Pengalih Output bisa dibuka oleh chip notifikasi media ketika aplikasi berada di latar belakang dan melakukan transmisi, aplikasi harus menangani transfer ke berbeda tergantung apakah mereka mendukung pemutaran di latar belakang atau tidak. Jika transfer gagal, onTransferFailed akan diaktifkan kapan saja error terjadi.

Aplikasi yang mendukung pemutaran di latar belakang

Untuk aplikasi yang mendukung pemutaran di latar belakang (biasanya aplikasi audio), sebaiknya gunakan Service (misalnya, MediaBrowserService). Layanan harus mendengarkan onTransferred callback dan melanjutkan pemutaran secara lokal, baik saat aplikasi berada di latar depan maupun latar belakang.

Kotlin
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.
        }
    }
}
Java
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.
        }
    }
}

Aplikasi yang tidak mendukung pemutaran di latar belakang

Untuk aplikasi yang tidak mendukung pemutaran di latar belakang (biasanya aplikasi video), disarankan untuk mendengarkan onTransferred dan melanjutkan pemutaran secara lokal jika aplikasi berjalan di latar depan.

Jika berada di latar belakang, aplikasi harus menjeda pemutaran dan menyimpan informasi yang diperlukan dari SessionState (misalnya, metadata media dan posisi pemutaran). Saat aplikasi dibuat menjadi latar depan dari latar belakang, pemutaran lokal akan dilanjutkan dengan informasi yang disimpan.

Kotlin
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.
        }
    }
}
Java
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.
    }
  }
}

Jarak jauh ke jarak jauh

Pengalih Output mendukung kemampuan untuk diperluas ke beberapa fitur yang kompatibel untuk Cast perangkat speaker untuk aplikasi audio yang menggunakan Ekspansi Streaming.

Aplikasi audio adalah aplikasi yang mendukung Google Cast untuk Audio di setelan Aplikasi Penerima di Konsol Developer Google Cast SDK

Perluasan Streaming dengan speaker

Aplikasi audio yang menggunakan Pengalih Output dapat memperluas audio ke beberapa perangkat speaker yang kompatibel untuk Cast selama sesi Cast menggunakan Streaming Perluasan.

Fitur ini didukung oleh platform Cast dan tidak memerlukan berubah jika aplikasi menggunakan UI default. Jika UI kustom digunakan, aplikasi harus mengupdate UI untuk mencerminkan bahwa aplikasi melakukan transmisi ke grup.

Untuk mendapatkan nama grup yang diperluas baru selama perluasan streaming, daftarkan Cast.Listener menggunakan CastSession#addCastListener. Lalu panggil CastSession#getCastDevice() selama callback onDeviceNameChanged.

Kotlin
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)
    }
}
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);
    }
}

Menguji jarak jauh ke jarak jauh

Untuk menguji fitur:

  1. Transmisikan konten ke perangkat yang kompatibel untuk Cast menggunakan transmisi konvensional atau dengan lokal ke jarak jauh.
  2. Buka Pengalih Output menggunakan salah satu titik entri.
  3. Ketuk perangkat lain yang kompatibel untuk Cast, aplikasi audio akan memperluas konten ke perangkat tambahan, sehingga membuat grup dinamis.
  4. Ketuk perangkat yang kompatibel untuk Cast lagi, perangkat tersebut akan dihapus dari dinamis ras.