Sélecteur de sortie

Le sélecteur de sortie est une fonctionnalité du SDK Cast qui facilite le transfert entre la lecture locale et à distance du contenu à partir d'Android 13. L’objectif permet aux applications émettrices de contrôler facilement et rapidement la lecture du contenu. Le sélecteur de sortie utilise bibliothèque MediaRouter pour activer ou désactiver la lecture du contenu sur le haut-parleur du téléphone, sur les appareils Bluetooth associés et les appareils compatibles Cast distants. Les cas d'utilisation peuvent être répartis comme suit : scénarios:

Téléchargez et utilisez l'application exemple CastVideos-pour Android. pour savoir comment implémenter le sélecteur de sortie dans votre application.

Le sélecteur de sortie doit être activé pour prendre en charge le passage du local au local à distance, et inversement et de la télécommande à la télécommande en suivant les étapes décrites dans ce guide. Il n'y a aucun étapes supplémentaires nécessaires pour permettre le transfert entre l'appareil local haut-parleurs et appareils Bluetooth associés.

UI du sélecteur de sortie

Le sélecteur de sortie affiche les appareils locaux et distants disponibles. ainsi que l'état actuel de l'appareil, y compris si l'appareil est sélectionné, se connecte, le niveau de volume actuel. S'il existe d'autres appareils en plus vers l'appareil actuel, cliquez sur "Autre appareil" pour transférer le contenu multimédia lecture sur l'appareil sélectionné.

Problèmes connus

  • Les sessions multimédias créées pour une lecture locale seront ignorées et recréées lorsque vous passez à la notification du SDK Cast.

Points d'entrée

Notification multimédia

Si une application publie une notification multimédia avec MediaSession pour la lecture locale, un chip de notification s'affiche en haut à droite de la notification multimédia avec le nom de l'appareil (comme le haut-parleur du téléphone) sur lequel le contenu est actuellement lu. Un appui sur le chip de notification s'ouvre l'UI du système de la boîte de dialogue "Output Switcher" (Sélecteur de sortie).

Paramètres de volume

L'interface utilisateur du système de sélection de sortie peut également être déclenchée en cliquant sur boutons de volume physiques sur l'appareil, en appuyant sur l'icône des paramètres en bas, et appuyez sur le bouton "Play <App Name> sur l'<appareil Cast>" texte.

Résumé des étapes

Prérequis

  1. Migrez votre application Android existante vers AndroidX.
  2. Mettez à jour le build.gradle de votre application afin d'utiliser la version minimale requise de SDK Android Sender pour le sélecteur de sortie:
    dependencies {
      ...
      implementation 'com.google.android.gms:play-services-cast-framework:21.2.0'
      ...
    }
  3. L'appli est compatible avec les notifications multimédias.
  4. Appareil équipé d'Android 13.

Configurer les notifications multimédias

Pour utiliser le sélecteur de sortie, audio et applications vidéo sont nécessaires pour créer une notification multimédia permettant d'afficher l'état de la lecture et des commandes de lecture locale pour leurs contenus multimédias. Pour cela, vous devez créer MediaSession en paramétrant le MediaStyle avec le jeton de MediaSession, et en configurant les commandes multimédias sur .

Si vous n'utilisez pas MediaStyle ni MediaSession actuellement, l'extrait ci-dessous montre comment les configurer. Des guides sont disponibles pour configurer le média des rappels de session audio et vidéo applications:

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

De plus, pour renseigner la notification avec les informations concernant votre contenu multimédia, vous devez ajouter les métadonnées et l'état de la lecture à MediaSession.

Pour ajouter des métadonnées à MediaSession, utilisez setMetaData() et fournissez toutes les constantes MediaMetadata pertinentes pour vos contenus multimédias dans 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()
    );
}

Pour ajouter l'état de lecture à MediaSession, utilisez setPlaybackState() et fournissez toutes les constantes PlaybackStateCompat pertinentes pour vos contenus multimédias dans 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()
    );
}

Comportement des notifications de l'application vidéo

Applications vidéo ou audio qui ne sont pas compatibles avec la lecture en arrière-plan en local doivent adopter un comportement spécifique pour les notifications multimédias afin d'éviter tout problème avec envoi de commandes multimédias lorsque la lecture n'est pas possible:

  • Publier la notification multimédia lors de la lecture du contenu multimédia en local et que l'application est en cours d'exécution au premier plan.
  • Mettez en pause la lecture locale et fermez la notification lorsque l'application est dans en arrière-plan.
  • Lorsque l'application revient au premier plan, la lecture locale doit reprendre et la notification doit être publiée à nouveau.

Activer le sélecteur de sortie dans AndroidManifest.xml

Pour activer le sélecteur de sortie, MediaTransferReceiver doit être ajouté au AndroidManifest.xml de l'application. Si ce n'est pas le cas, la fonctionnalité ne sera pas activé et l'indicateur de fonctionnalité distant/local ne sera pas non plus valide.

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

La MediaTransferReceiver est un broadcast receiver qui permet le transfert multimédia entre des appareils UI. Pour en savoir plus, consultez la documentation de référence sur MediaTransferReceiver.

Local vers distant

Lorsque l'utilisateur passe de la lecture locale à la lecture à distance, le SDK Cast démarre automatiquement la session Cast. Toutefois, les applications doivent gérer cette migration "local" vers la version distante (par exemple, arrêter la lecture en local) et chargez le contenu multimédia sur l'appareil Cast. Les applications doivent écouter Cast SessionManagerListener à l'aide du onSessionStarted() et onSessionEnded() et gérer l'action à la réception de l'objet Cast SessionManager . Les applications doivent s'assurer que ces rappels sont toujours actifs lorsque la boîte de dialogue du sélecteur de sortie est ouverte et l'application n'est pas au premier plan.

Mettre à jour SessionManagerListener pour la diffusion en arrière-plan

L'ancienne expérience Cast est déjà compatible avec le stockage local/à distance lorsque l'application est au premier plan. En général, l'expérience Cast commence lorsque l'utilisateur clique sur l'icône Cast. dans l'application, puis sélectionnez un appareil sur lequel diffuser le contenu multimédia. Dans ce cas, l'application doit s'enregistrer auprès de SessionManagerListener, dans onCreate() ou onStart(), et désenregistrer l'écouteur dans onStop() ou onDestroy() de l'activité de l'application.

Grâce à la nouvelle expérience de diffusion basée sur le sélecteur de sortie, les applications peuvent si elles sont diffusées en arrière-plan. C'est particulièrement utile pour les fichiers audio Applications qui publient des notifications lorsqu'elles sont lues en arrière-plan Les applications peuvent enregistrer les écouteurs SessionManager dans le onCreate() du service et se désenregistrer dans le onDestroy() du service. Les applications doivent toujours recevoir les rappels locaux vers distants (tels que en tant que onSessionStarted) lorsque l'application est exécutée en arrière-plan.

Si l'application utilise MediaBrowserService, il est recommandé d'y enregistrer SessionManagerListener.

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

Avec cette mise à jour, le mode local vers distant fonctionne de la même manière que la diffusion traditionnelle lorsque le est exécutée en arrière-plan. Aucun travail supplémentaire n'est requis pour passer de les appareils Bluetooth vers les appareils Cast ;

Du distant au local

Le sélecteur de sortie permet de passer de la lecture à distance le haut-parleur du téléphone ou un appareil Bluetooth local. Vous pouvez activer cette fonctionnalité en paramétrant le setRemoteToLocalEnabled l'indicateur sur true sur CastOptions.

Dans les cas où l'appareil émetteur actuel rejoint une session existante avec plusieurs expéditeurs et l'application doit vérifier si le contenu multimédia actuel est autorisé à être transférées localement, les applications doivent utiliser le onTransferred rappel de SessionTransferCallback pour vérifier le SessionState.

Définir l'indicateur setRemoteToLocalEnabled

CastOptions.Builder fournit un setRemoteToLocalEnabled pour afficher ou masquer le haut-parleur du téléphone et les appareils Bluetooth locaux en tant que cibles de transfert dans la boîte de dialogue du sélecteur de sortie lorsqu'une session Cast est active.

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()
  }
}

Continuer la lecture en local

Les applications qui prennent en charge le passage à distance à distance doivent enregistrer le SessionTransferCallback pour recevoir une notification lorsque l'événement se produit et vérifier si les contenus multimédias doivent être autorisé à transférer et poursuivre la lecture en local.

CastContext#addSessionTransferCallback(SessionTransferCallback) permet à une application d'enregistrer ses SessionTransferCallback et écouter les rappels onTransferred et onTransferFailed lorsqu'un expéditeur transféré vers la lecture locale.

Une fois que l'application a annulé l'enregistrement de ses SessionTransferCallback, l'application ne recevra plus SessionTransferCallback .

SessionTransferCallback est une extension de la propriété SessionManagerListener existante et est déclenché après le déclenchement de onSessionEnded. L'ordre des rappels à distance vers le local est le suivant :

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

Le sélecteur de sortie pouvant être ouvert par le chip de notification multimédia lorsque est exécutée en arrière-plan et caste, les applications doivent gérer le transfert en local selon qu'ils sont compatibles ou non avec la lecture en arrière-plan. Dans l'étui d'un échec de transfert, onTransferFailed se déclenche chaque fois que l'erreur se produit.

Applis compatibles avec la lecture en arrière-plan

Pour les applications qui prennent en charge la lecture en arrière-plan (généralement des applications audio), il est il est recommandé d'utiliser un Service (par exemple, MediaBrowserService). Services doit écouter onTransferred et reprendre la lecture localement lorsque l'application est exécutée au premier plan ou en arrière-plan.

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

Applis non compatibles avec la lecture en arrière-plan

Pour les applications qui ne prennent pas en charge la lecture en arrière-plan (généralement des applications vidéo), il est recommandé d'écouter onTransferred et reprendre la lecture localement si l'application est exécutée au premier plan.

Si l'application est en arrière-plan, elle doit mettre en pause la lecture et stocker les informations nécessaires à partir de SessionState (par exemple, les métadonnées multimédias et la position de lecture). Lorsque l'application est avec l'arrière-plan au premier plan, la lecture locale doit se poursuivre des informations stockées.

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

À distance

Le sélecteur de sortie permet d'étendre la sortie à plusieurs appareils compatibles Cast. enceintes pour les applications audio qui utilisent la fonctionnalité d'expansion du flux.

Les applications audio sont celles qui prennent en charge Google Cast pour l'audio dans les paramètres de l'application réceptrice dans la console développeur du SDK Google Cast.

Diffusion étendue avec les enceintes

Les applications audio qui utilisent le sélecteur de sortie peuvent diffuser l'audio sur plusieurs appareils de haut-parleurs compatibles avec Cast lors d'une session Cast à l'aide de l'extension de flux.

Cette fonctionnalité est compatible avec la plate-forme Cast et ne nécessite aucune autre action change si l'application utilise l'interface utilisateur par défaut. Si une UI personnalisée est utilisée, l'application doit mettre à jour l'UI pour indiquer qu'elle diffuse du contenu vers un groupe.

Pour obtenir le nouveau nom de groupe développé lors du développement du streaming, procédez comme suit : enregistrer un Cast.Listener à l'aide du CastSession#addCastListener Appelez ensuite CastSession#getCastDevice() lors du rappel 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);
    }
}

Tester un transfert à distance à distance

Pour tester la fonctionnalité :

  1. Castez vos contenus sur un appareil compatible Cast en utilisant les fonctionnalités de diffusion conventionnelles ou avec local-to-remote
  2. Ouvrez le sélecteur de sortie à l'aide de l'un des points d'entrée.
  3. Appuyez sur un autre appareil compatible Cast pour afficher le contenu un appareil supplémentaire, créant ainsi un groupe dynamique.
  4. Appuyez de nouveau sur l'appareil compatible Cast. Il est alors supprimé de l'environnement dynamique. groupe.