El selector de salida es una función del SDK de Cast que permite transferir contenido de forma fluida entre la reproducción local y remota a partir de Android 13. El objetivo es ayudar a las apps de remitentes a controlar de forma fácil y rápida dónde se reproduce el contenido.
El selector de salida usa la biblioteca MediaRouter
para cambiar la reproducción de contenido entre la bocina del teléfono, los dispositivos Bluetooth vinculados y los dispositivos remotos compatibles con Cast. Los casos de uso se pueden dividir en los siguientes elementos:
diferentes:
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 funciones de local a remoto y de remoto a local y de remoto a remoto siguiendo los pasos que se indican en esta guía. No hay pasos adicionales necesarios para admitir la transferencia entre el dispositivo local bocinas y 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 del dispositivo (incluso si está seleccionado) se está conectando, el nivel actual del volumen. Si hay otros dispositivos además al dispositivo actual; si haces clic en otro dispositivo, podrás transferir el contenido multimedia la reproducción en el dispositivo seleccionado.
Problemas conocidos
- Las sesiones multimedia creadas para la reproducción local se descartarán y se volverán a crear. cuando cambias a la notificación del SDK de Cast.
Puntos de entrada
Notificación multimedia
Si una app publica una notificación multimedia con
MediaSession
para
Reproducción local (reproduciendo localmente), en la esquina superior derecha de la notificación multimedia
muestra un chip de notificación con el nombre del dispositivo (como el altavoz del teléfono) que
se está reproduciendo el contenido en ese momento. Si presionas el chip de notificaciones, se abrirá
la IU del sistema del diálogo Output Switcher.
Configuración del volumen
La IU del sistema del diálogo Output Switcher también se puede activar haciendo clic en el botón botones de volumen físicos del dispositivo y tocar el ícono de configuración en la parte inferior y presionando el botón "Play <App Name> en <Dispositivo de transmisión>" texto.
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
- Cómo agregar compatibilidad con Remote-to-Remote
- Configura la marca setRemoteToLocalEnabled
- Cómo continuar la reproducción de forma local
Requisitos previos
- Migra tu app para Android existente a AndroidX.
- Actualiza el
build.gradle
de tu app para usar la versión mínima requerida de la 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 multimedia.
- Dispositivo con Android 13
Configura las notificaciones multimedia
Para usar el selector de salida, las apps de audio y video deben crear una notificación 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
:
estableciendo
MediaStyle
usando el token de MediaSession
y configurando los controles multimedia en la
notificación.
Si actualmente no usas un MediaStyle
ni un MediaSession
, el fragmento
a continuación, se muestra cómo configurarlos, y hay guías disponibles para configurar el contenido multimedia
de devoluciones de llamada de sesión
audio y
video
apps:
// 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 del contenido multimedia a MediaSession
.
Para agregar metadatos a MediaSession
, usa
setMetaData()
y proporciona todos los documentos
Constantes MediaMetadata
para
tus medios en la
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 a MediaSession
, usa
setPlaybackState()
y proporciona todos los documentos
PlaybackStateCompat
constantes para tus medios 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 apps de video
Las apps de video o audio que no admiten la reproducción local en segundo plano deben tener un comportamiento específico para las notificaciones multimedia para evitar problemas con el envío de comandos multimedia en situaciones en las que no se admite la reproducción:
- Publica la notificación multimedia cuando se reproduce contenido multimedia de forma local y la app está en en primer plano.
- Pausa la reproducción local y descarta la notificación cuando la app esté en en segundo plano.
- Cuando la app vuelve al primer plano, se debe reanudar la reproducción local y la notificación debe volver a publicarse.
Habilita el selector de salida en AndroidManifest.xml
Para habilitar el selector de salida,
MediaTransferReceiver
debe agregarse al AndroidManifest.xml
de la app. Si no lo es, 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>
El
MediaTransferReceiver
es un receptor de emisión que permite la transferencia de contenido multimedia entre dispositivos con
de la IU de Google. Consulta la referencia de MediaTransferReceiver para obtener más información.
De local a remoto
Cuando el usuario cambie la reproducción de local a remoto, el SDK de Cast comenzará
automáticamente la sesión de transmisión. Sin embargo, las apps deben controlar el cambio
De local a remoto (por ejemplo, detener la reproducción local)
y carga el contenido multimedia en el dispositivo de transmisión. Las apps deberían escuchar la transmisión
SessionManagerListener
:
con el
onSessionStarted()
y
onSessionEnded()
las devoluciones de llamada y controlar la acción cuando recibe la transmisión
SessionManager
devoluciones de llamada. Las apps deben asegurarse de que estas devoluciones de llamada sigan activas cuando
Se abre el diálogo Output Switcher y la app no está en primer plano.
Actualiza SessionManagerListener para la transmisión en segundo plano
La experiencia heredada de transmisión ya admite la conexión de local a remoto cuando la app se
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 necesita
regístrate en
SessionManagerListener
:
en onCreate()
o
onStart()
y cancelar el registro del objeto de escucha en
onStop()
o
onDestroy()
de la actividad de la aplicación.
Con la nueva experiencia de transmisión con el selector de salida, las apps pueden comenzar
transmitiendo cuando están en segundo plano. Esto es particularmente útil para audio
Apps que publican notificaciones cuando se reproduce en segundo plano. Las apps pueden registrarse
el SessionManager
objetos de escucha en el onCreate()
del servicio y cancelar el registro en 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 MediaBrowserService
, se recomienda registrar 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 opción de local a remoto actúa 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 para transmitir
De remoto a local
El selector de salida permite transferir la reproducción remota a la bocina del teléfono o al dispositivo Bluetooth local. Para habilitar esto, establece la marca setRemoteToLocalEnabled
en true
en CastOptions
.
Para los casos en los que el dispositivo emisor actual se une a una sesión existente con
varios remitentes y la app debe comprobar si el contenido multimedia actual tiene permitido
se transfieran de forma local, las apps deben usar el onTransferred
devolución de llamada de SessionTransferCallback
para consultar el SessionState
.
Configura la marca setRemoteToLocalEnabled
El CastOptions.Builder
proporciona un setRemoteToLocalEnabled
para mostrar u ocultar la bocina del teléfono y los dispositivos Bluetooth locales como
objetivos en el diálogo Output Switcher cuando haya una sesión de transmisión 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() } }
Continuar la reproducción de forma local
Las apps que admiten la opción de remoto a local deben registrar el SessionTransferCallback
.
para recibir una notificación cuando ocurra el evento, de modo que puedan comprobar si el contenido multimedia debe
pueden transferirse y continuar la reproducción de forma local.
CastContext#addSessionTransferCallback(SessionTransferCallback)
permite que una app registre su SessionTransferCallback
y busca devoluciones de llamada onTransferred
y onTransferFailed
cuando un remitente
se transfiera a la reproducción local.
Después de que la app cancela el registro de su SessionTransferCallback
,
la app ya no recibirá SessionTransferCallback
.
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 tienen las siguientes características:
onTransferring
onSessionEnding
onSessionEnded
onTransferred
Dado que el selector de salida se puede abrir con el chip de notificación multimedia cuando
app está en segundo plano y transmitiendo, las apps deben controlar la transferencia a
de manera diferente según si admiten o no la reproducción en segundo plano. En el caso
de una transferencia con errores, onTransferFailed
se activará cada vez que ocurra el error.
Apps que admiten la reproducción en segundo plano
En el caso de las aplicaciones que admiten la reproducción en segundo plano (por lo general, las aplicaciones de audio), es
Se recomienda usar un Service
(por ejemplo, MediaBrowserService
). Servicios
deberías escuchar 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, las apps de video),
recomendó escuchar onTransferred
y reanudar la reproducción de forma local si la app está en primer plano.
Si la app está en segundo plano, debería pausar la reproducción y almacenar
la información necesaria de SessionState
(por ejemplo, los metadatos del contenido multimedia y la posición de reproducción). Cuando la app esté
en primer plano desde el segundo plano, la reproducción local debería continuar con la
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. } } }
De remoto a remoto
El selector de salida admite la capacidad de expandirse a varias opciones bocinas para apps de audio que usan la expansión de transmisión.
Las apps de audio son compatibles con Google Cast para audio en la app receptora. en la configuración del SDK de Google Cast para desarrolladores Consola
Expansión de transmisión con bocinas
Las apps de audio que usan el Selector de salida pueden expandir el audio a varias bocinas compatibles con Cast durante una sesión de transmisión mediante Transmitir Expansió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 Debes actualizar la IU para reflejar que la app está transmitiendo a un grupo.
Para obtener el nuevo nombre del grupo expandido durante una expansión de transmisión, sigue estos pasos:
registrar un
Cast.Listener
con el
CastSession#addCastListener
Luego, llama
CastSession#getCastDevice()
durante la devolución de llamada onDeviceNameChanged
.
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 contenido a un dispositivo compatible con Cast mediante una transmisión convencional o con local-to-remote.
- Abre el selector de salida usando uno de los puntos de entrada.
- Presiona otro dispositivo compatible con Cast. Las apps de audio expandirán el contenido al dispositivo adicional, lo que crea un grupo dinámico.
- Vuelve a presionar el dispositivo compatible con Cast para quitarlo del grupo dinámico.