El Selector de salida es una función del SDK de Cast que permite la transferencia sin problemas entre la reproducción local y remota de contenido a partir de Android 13. El objetivo es ayudar a las apps emisoras a controlar de forma fácil y rápida dónde se reproduce el contenido.
El Selector de salida usa la
MediaRouter biblioteca para
cambiar la reproducción de contenido entre el altavoz del teléfono, los dispositivos Bluetooth vinculados
y los dispositivos remotos compatibles con Cast. Los casos de uso se pueden dividir en las siguientes situaciones:
Descarga y usa la app de ejemplo CastVideos-android como referencia para implementar el Selector de salida en tu app.
El Selector de salida debe estar habilitado para admitir la transferencia de local a remoto, de remoto a local y de remoto a remoto con los pasos que se explican en esta guía. No se necesitan pasos adicionales para admitir la transferencia entre los altavoces del dispositivo local y los dispositivos Bluetooth vinculados.
IU del Selector de salida
El Selector de salida muestra los dispositivos locales y remotos que están disponibles, así como los estados actuales de los dispositivos, incluido si el dispositivo está seleccionado, se está conectando o el nivel de volumen actual. Si hay otros dispositivos además del dispositivo actual, al hacer clic en otro dispositivo, se permite transferir la reproducción de contenido multimedia al dispositivo seleccionado.

Problemas conocidos
- Las sesiones de contenido multimedia creadas para la reproducción local se descartarán y se volverán a crear cuando se cambie a la notificación del SDK de Cast.
Puntos de entrada
Notificación de contenido multimedia
Si una app publica una notificación de contenido multimedia con
MediaSession para
la reproducción local (reproducción local), la esquina superior derecha de la notificación de contenido multimedia
muestra un chip de notificación con el nombre del dispositivo (como el altavoz del teléfono) en el que
se reproduce el contenido actualmente. Si presionas el chip de notificación, se abrirá la IU del sistema del diálogo del Selector de salida.

Configuración del volumen
También se puede activar la IU del sistema del diálogo del Selector de salida haciendo clic en los botones físicos de volumen del dispositivo, presionando el ícono de configuración en la parte inferior y presionando el texto "Reproducir <App Name> en <Cast Device>".

Resumen de los pasos
- Asegúrate de que se cumplan los requisitos previos.
- Habilita el Selector de salida en AndroidManifest.xml.
- Actualiza SessionManagerListener para la transmisión en segundo plano.
- Agrega compatibilidad con la transferencia de remoto a remoto.
- Establece la marca setRemoteToLocalEnabled.
- Continúa reproduciendo de forma local.
Requisitos previos
- Migra tu app para Android existente a AndroidX.
- Actualiza el archivo
build.gradlede tu app para usar la versión mínima requerida del SDK de Android Sender para el Selector de salida:dependencies { ... implementation 'com.google.android.gms:play-services-cast-framework:21.2.0' ... }
- La app admite notificaciones de contenido multimedia.
- El dispositivo ejecuta Android 13.
Configura las notificaciones de contenido multimedia
Para usar el Selector de salida,
las apps de audio y
video
deben crear una notificación de contenido multimedia para mostrar el estado de reproducción y los
controles de su contenido multimedia para la reproducción local. Esto requiere crear un
MediaSession,
establecer el
MediaStyle
con el token de MediaSession, y configurar los controles de contenido multimedia en la
notificación.
Si actualmente no usas un MediaStyle y un MediaSession, el siguiente fragmento
muestra cómo configurarlos y hay guías disponibles para configurar las devoluciones de llamada de la sesión de contenido multimedia
para
apps de audio y
video:
// 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(); }

Además, para propagar la notificación con la información de tu contenido multimedia,
deberás agregar los
metadatos y el estado de reproducción
de tu contenido multimedia a MediaSession.
Para agregar metadatos al MediaSession, usa
setMetaData()
y proporciona todas las constantes
MediaMetadata relevantes para
tu contenido multimedia en el
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() ); }

Para agregar el estado de reproducción al MediaSession, usa
setPlaybackState()
y proporciona todas las constantes
PlaybackStateCompat
relevantes para tu contenido multimedia en el
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() ); }

Comportamiento de las notificaciones de la app de video
Las apps de video o las apps de audio que no admiten la reproducción local en segundo plano deben tener un comportamiento específico para las notificaciones de contenido multimedia para evitar problemas con el envío de comandos de contenido multimedia en situaciones en las que no se admite la reproducción:
- Publica la notificación de contenido multimedia cuando se reproduce contenido multimedia de forma local y la app está en primer plano.
- Pausa la reproducción local y descarta la notificación cuando la app esté en segundo plano.
- Cuando la app vuelva a primer plano, se debe reanudar la reproducción local y se debe volver a publicar la notificación.
Habilita el Selector de salida en AndroidManifest.xml
Para habilitar el Selector de salida, el
MediaTransferReceiver
debe agregarse al AndroidManifest.xmlde la app. Si no lo haces, la función no se habilitará y la marca de función de remoto a local tampoco será válida.
<application>
...
<receiver
android:name="androidx.mediarouter.media.MediaTransferReceiver"
android:exported="true">
</receiver>
...
</application>
The
MediaTransferReceiver
es un receptor de transmisión que permite la transferencia de contenido multimedia entre dispositivos con la IU del sistema. Consulta la referencia de MediaTransferReceiver
para obtener más información.
Local a remoto
Cuando el usuario cambia la reproducción de local a remoto, el SDK de Cast iniciará la sesión de Cast automáticamente. Sin embargo, las apps deben controlar el cambio de local a remoto, por ejemplo, detener la reproducción local y cargar el contenido multimedia en el dispositivo de Cast. Las apps deben escuchar el Cast
SessionManagerListener,
usando las devoluciones de llamada
onSessionStarted()
y
onSessionEnded(), y controlar la acción cuando reciban las devoluciones de llamada
SessionManager
de Cast. Las apps deben asegurarse de que estas devoluciones de llamada sigan activas cuando se abra el diálogo del Selector de salida y la app no esté en primer plano.
Actualiza SessionManagerListener para la transmisión en segundo plano
La experiencia heredada de Cast ya admite la transferencia de local a remoto cuando la app está en primer plano. Una experiencia típica de Cast comienza cuando los usuarios hacen clic en el ícono de Cast en la app y eligen un dispositivo para transmitir contenido multimedia. En este caso, la app debe
registrarse en el
SessionManagerListener,
en onCreate() o
onStart()
y anular el registro del objeto de escucha en
onStop()
o
onDestroy()
de la actividad de la app.
Con la nueva experiencia de transmisión con el Selector de salida, las apps pueden comenzar a transmitir cuando están en segundo plano. Esto es especialmente útil para las apps de audio que publican notificaciones cuando se reproducen en segundo plano. Las apps pueden registrar
los SessionManager
objetos de escucha en el onCreate() del servicio y anular el registro en el onDestroy()
del servicio. Las apps siempre deben recibir las devoluciones de llamada de local a remoto (como
onSessionStarted)
cuando la app está en segundo plano.
Si la app usa el
MediaBrowserService,
se recomienda registrar el SessionManagerListener
allí.
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); } } }
Con esta actualización, la transferencia de local a remoto funciona de la misma manera que la transmisión convencional cuando la app está en segundo plano y no se requiere trabajo adicional para cambiar de dispositivos Bluetooth a dispositivos de Cast.
Remoto a local
El Selector de salida proporciona la capacidad de transferir de la reproducción remota al altavoz del teléfono o al dispositivo Bluetooth local. Para habilitar esta opción, establece la
setRemoteToLocalEnabled
en true en el CastOptions.
En los casos en los que el dispositivo emisor actual se une a una sesión existente con
varios emisores y la app necesita verificar si se permite transferir el contenido multimedia actual de forma
local, las apps deben usar la onTransferred
devolución de llamada del SessionTransferCallback
para verificar el SessionState.
Establece la marca setRemoteToLocalEnabled
El CastOptions.Builder
proporciona un setRemoteToLocalEnabled para mostrar u ocultar el altavoz del teléfono y los dispositivos Bluetooth locales como destinos de transferencia
en el diálogo del Selector de salida cuando hay una sesión de Cast activa.
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() } }
Continúa reproduciendo de forma local
Las apps que admiten la transferencia de remoto a local deben registrar el SessionTransferCallback
para recibir notificaciones cuando se produce el evento, de modo que puedan verificar si se debe
permitir la transferencia de contenido multimedia y continuar reproduciendo de forma local.
CastContext#addSessionTransferCallback(SessionTransferCallback)
permite que una app registre su SessionTransferCallback
y escuche las devoluciones de llamada onTransferred y onTransferFailed cuando se transfiere un emisor a la reproducción local.
Después de que la app anule el registro de su SessionTransferCallback,
ya no recibirá SessionTransferCallback.
El SessionTransferCallback
es una extensión de las devoluciones de llamada SessionManagerListener
existentes y se activa después de que se activa onSessionEnded. El orden de las devoluciones de llamada de remoto a local es el siguiente:
onTransferringonSessionEndingonSessionEndedonTransferred
Dado que el Selector de salida se puede abrir con el chip de notificación de contenido multimedia cuando la app está en segundo plano y transmitiendo, las apps deben controlar la transferencia a local de manera diferente según si admiten la reproducción en segundo plano o no. En caso
de que falle la transferencia, onTransferFailed
se activará en cualquier momento en que se produzca el error.
Apps que admiten la reproducción en segundo plano
En el caso de las apps que admiten la reproducción en segundo plano (por lo general, apps de audio), se
recomienda usar un Service (por ejemplo, MediaBrowserService). Los servicios
deben escuchar la devolución de llamada onTransferred
y reanudar la reproducción de forma local cuando la app esté en primer plano o
en segundo plano.
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. } } }
Apps que no admiten la reproducción en segundo plano
En el caso de las apps que no admiten la reproducción en segundo plano (por lo general, apps de video), se
recomienda escuchar la onTransferred
devolución de llamada y reanudar la reproducción de forma local si la app está en primer plano.
Si la app está en segundo plano, debe pausar la reproducción y almacenar la
información necesaria de SessionState
(por ejemplo, metadatos de contenido multimedia y posición de reproducción). Cuando la app pasa de segundo plano a primer plano, la reproducción local debe continuar con la información almacenada.
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. } } }
Remoto a remoto
El Selector de salida admite la capacidad de expandirse a varios dispositivos de bocinas compatibles con Cast para apps de audio que usan la Expansión de transmisión.
Las apps de audio son apps que admiten Google Cast para audio en la configuración de la app receptora en la Consola del SDK de Google Cast para desarrolladores

Expansión de transmisión con bocinas
Las apps de audio que usan el Selector de salida tienen la capacidad de expandir el audio a varios dispositivos de bocinas compatibles con Cast durante una sesión de Cast con la Expansión de transmisión.
La plataforma de Cast admite esta función y no requiere más cambios si la app usa la IU predeterminada. Si se usa una IU personalizada, la app debe actualizar la IU para reflejar que la app está transmitiendo a un grupo.

Para obtener el nuevo nombre de grupo expandido durante una expansión de transmisión,
registra un
Cast.Listener
con el
CastSession#addCastListener.
Luego, llama a
CastSession#getCastDevice()
durante la onDeviceNameChanged devolución de llamada.
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); } }
Prueba de remoto a remoto
Para probar la función, haz lo siguiente:
- Transmite tu contenido a un dispositivo compatible con Cast con la transmisión convencional o con la transferencia de local a remoto.
- Abre el Selector de salida con uno de los puntos de entrada.
- Presiona otro dispositivo compatible con Cast. Las apps de audio expandirán el contenido al dispositivo adicional y crearán un grupo dinámico.
- Vuelve a presionar el dispositivo compatible con Cast. Se quitará del grupo dinámico.