1. Présentation
Dans cet atelier de programmation, vous allez apprendre à modifier une application vidéo Android existante afin de caster du contenu sur un appareil compatible Google Cast.
Qu'est-ce que Google Cast ?
Google Cast permet aux utilisateurs de caster du contenu multimédia depuis un appareil mobile sur un téléviseur, et d'utiliser leur appareil mobile comme télécommande lors de la diffusion.
Le SDK Google Cast vous permet d'étendre les fonctionnalités de votre application afin de contrôler un téléviseur ou un système audio. Il vous permet d'ajouter les composants d'UI (interface utilisateur) requis, en fonction de la checklist de conception Google Cast.
La checklist de conception de Google Cast a été conçue pour garantir une expérience utilisateur simple et prévisible sur toutes les plates-formes compatibles.
Qu'allons-nous créer ?
À la fin de cet atelier de programmation, vous disposerez d'une application vidéo Android capable de caster des vidéos sur un appareil Google Cast.
Points abordés
- Comment ajouter le SDK Google Cast à un exemple d'application vidéo
- Comment ajouter l'icône Cast permettant de sélectionner un appareil Google Cast
- Comment se connecter à un appareil Cast et lancer un récepteur multimédia
- Comment caster une vidéo
- Comment ajouter une mini-télécommande Cast à votre appli
- Comment gérer les notifications du contenu multimédia et les commandes de l'écran de verrouillage
- Comment ajouter une télécommande agrandie
- Comment afficher un message en superposition pour annoncer le lancement de la fonctionnalité Cast
- Comment personnaliser les widgets Cast
- Intégrer Cast Connect
Prérequis
- La dernière version du SDK Android
- Android Studio, version 3.2 ou ultérieure
- Un appareil mobile équipé d'Android 4.1 Jelly Bean ou ultérieure (niveau d'API 16)
- Un câble de données USB pour connecter votre appareil mobile à votre ordinateur de développement
- Un appareil Google Cast, comme Chromecast ou Android TV, configuré pour accéder à Internet.
- Un téléviseur ou un moniteur doté d'une entrée HDMI
- Un Chromecast avec Google TV est nécessaire pour tester l'intégration de Cast Connect, mais il est facultatif pour le reste de l'atelier de programmation. Si vous n'en avez pas, n'hésitez pas à ignorer l'étape Ajouter la prise en charge de Cast Connect, vers la fin de ce tutoriel.
Expérience
- Vous devez avoir une connaissance préalable du développement Kotlin et d'Android.
- Vous devez également avoir une expérience préalable en tant que téléspectateur :)
Comment allez-vous utiliser ce tutoriel ?
Comment évalueriez-vous votre niveau d'expérience en matière de création d'applications Android ?
Comment évalueriez-vous votre expérience en tant que téléspectateur ?
2. Obtenir l'exemple de code
Vous pouvez télécharger tout l'exemple de code sur votre ordinateur…
puis décompresser le fichier ZIP téléchargé.
3. Exécuter l'application exemple
Voyons d'abord comment se présente notre exemple d'application une fois terminée. L'appli est un lecteur vidéo de base. L'utilisateur peut sélectionner une vidéo à partir d'une liste, puis la lire en local sur l'appareil ou la caster sur un appareil Google Cast.
Une fois le code téléchargé, suivez les instructions ci-dessous pour ouvrir et exécuter l'exemple d'application terminée dans Android Studio:
Sélectionnez Import Project (Importer un projet) sur l'écran d'accueil ou passez par les options du menu File > New > Import Project… (Fichier > Nouveau > Importer un projet…).
Sélectionnez le répertoire app-done
dans le dossier de l'exemple de code, puis cliquez sur "OK".
Cliquez sur File (Fichier) > Sync Project with Gradle Files (Synchroniser le projet avec les fichiers Gradle).
Activez le débogage USB sur votre appareil Android. Notez que l'écran des options pour les développeurs est masqué par défaut dans la version Android 4.2 et les versions ultérieures. Pour le rendre visible, accédez à Paramètres > À propos du téléphone et appuyez sept fois sur Numéro de build. Revenez ensuite à l'écran précédent et sélectionnez Système > Paramètres avancés. En bas de l'écran, appuyez sur Options pour les développeurs, puis sur l'option Débogage USB pour l'activer.
Branchez votre appareil Android et cliquez sur le bouton Run (Exécuter) dans Android Studio. L'application vidéo intitulée Cast Videos devrait apparaître au bout de quelques secondes.
Cliquez sur l'icône Cast dans l'appli vidéo, puis sélectionnez votre appareil Google Cast.
Après avoir choisi une vidéo, cliquez sur le bouton de lecture.
La vidéo démarrera alors sur votre appareil Google Cast.
Et une télécommande agrandie s'affichera en plein écran sur votre appareil mobile. À l'aide du bouton de lecture/pause, vous pouvez contrôler la diffusion en cours.
Revenez à la liste des vidéos.
Une mini-télécommande est maintenant visible au bas de l'écran.
Cliquez sur le bouton "Pause" de la mini-télécommande pour mettre la vidéo en pause sur le récepteur. Pour continuer de regarder la vidéo, cliquez de nouveau sur le bouton de lecture de la mini-télécommande.
Cliquez sur le bouton d'accueil de l'appareil mobile. Déroulez les notifications depuis le haut de l'écran : une notification concernant votre session Cast en cours devrait maintenant s'afficher.
Verrouillez votre téléphone, puis déverrouillez-le : une notification vous permettant de contrôler la lecture du contenu multimédia ou d'arrêter la diffusion devrait maintenant apparaître sur l'écran de verrouillage.
Revenez à l'appli vidéo, puis cliquez sur l'icône Cast pour arrêter la diffusion sur l'appareil Google Cast.
Questions fréquentes
4. Préparer le projet de départ
Nous objectif est de rendre l'application de départ que vous avez téléchargée compatible avec Google Cast. Au cours de cet atelier de programmation, nous utiliserons la terminologie Google Cast suivante :
- Une appli de type émetteur s'exécute sur un appareil mobile ou un ordinateur portable.
- Une appli de type récepteur s'exécute sur l'appareil Google Cast.
Vous voici prêt à poursuivre le développement du projet de départ en utilisant Android Studio :
- Sélectionnez le répertoire
app-start
à partir de votre exemple de code téléchargé (sélectionnez Import Project (Importer un projet) sur l'écran d'accueil ou l'option de menu File > New > Import Project (Fichier > Nouveau > Importer un projet). - Cliquez sur le bouton Sync Project with Gradle Files (Synchroniser le projet avec les fichiers Gradle).
- Cliquez sur le bouton Run (Exécuter) pour exécuter l'application et explorer l'interface utilisateur.
Conception d'applications
L'application récupère une liste de vidéos à partir d'un serveur Web distant pour fournir une liste à parcourir à l'utilisateur. Ce dernier peut alors sélectionner une vidéo pour en afficher les détails ou la lire localement sur son appareil mobile.
L'application peut effectuer deux activités principales : VideoBrowserActivity
et LocalPlayerActivity
. Pour pouvoir intégrer la fonctionnalité Google Cast, les activités doivent hériter de AppCompatActivity
ou bien de son parent FragmentActivity
. Cette restriction vient du fait que nous devrons ajouter l'élément MediaRouteButton
(fourni dans la bibliothèque Support MediaRouter) en tant que MediaRouteActionProvider
, ce qui ne fonctionnera que si l'activité hérite des classes mentionnées ci-dessus. En effet, la bibliothèque Support MediaRouter dépend de la bibliothèque Support AppCompat qui fournit les classes requises.
VideoBrowserActivity
Cette activité contient un Fragment
(VideoBrowserFragment
). Cette liste est soutenue par un ArrayAdapter
(VideoListAdapter
). La liste des vidéos et les métadonnées qui y sont associées sont hébergées sur un serveur distant, sous la forme d'un fichier JSON. Un AsyncTaskLoader
(VideoItemLoader
) récupère ce fichier JSON, puis le traite pour créer une liste d'objets MediaItem
.
Un objet MediaItem
sert à modéliser une vidéo, ainsi que les métadonnées qui lui sont associées telles que le titre, la description, l'URL du flux, l'URL des images associées et les pistes de texte (pour les sous-titres), le cas échéant. Comme l'objet MediaItem
est transmis d'une activité à l'autre, il dispose de méthodes utilitaires qui permettent de le convertir en Bundle
, et inversement.MediaItem
Une fois que le loader a créé la liste de MediaItems
, il la transmet au VideoListAdapter
, qui la présente ensuite dans le VideoBrowserFragment
. Pour l'utilisateur, une liste de vignettes vidéos, où chaque vidéo est accompagnée d'une brève description, s'affiche à l'écran. Lorsqu'un élément est sélectionné, le MediaItem
correspondant est converti en Bundle
et transmis à LocalPlayerActivity
.
LocalPlayerActivity
Cette activité affiche les métadonnées associées à une vidéo particulière, et permet à l'utilisateur de lire la vidéo localement sur son appareil mobile.
Elle héberge également une VideoView
, des commandes multimédias et une zone de texte permettant d'afficher la description de la vidéo sélectionnée. Le lecteur vidéo occupe la partie supérieure de l'écran, laissant ainsi la place d'ajouter une description détaillée de la vidéo en dessous. L'utilisateur peut lire la vidéo choisie, la mettre en pause ou rechercher d'autres vidéos à visionner localement.
Dépendances
Comme nous utilisons AppCompatActivity
, nous avons besoin de la bibliothèque Support AppCompat. Pour gérer la liste de vidéos et obtenir les images de la liste de manière asynchrone, nous utilisons la bibliothèque Volley.
Questions fréquentes
5. Ajouter l'icône Cast
Une application compatible Cast affiche l'icône Cast dans chacune de ses activités. Lorsque l'utilisateur clique sur cette icône, la liste des appareils Cast qu'il peut sélectionner s'affiche. Si un contenu était en cours de lecture localement sur l'appareil émetteur, le fait de sélectionner un appareil Cast démarre ou reprend cette même lecture directement sur l'appareil Cast sélectionné. À tout moment, l'utilisateur doit pouvoir cliquer sur l'icône Cast pour interrompre la diffusion émise à partir de votre application sur l'appareil Cast. L'utilisateur doit pouvoir se connecter à l'appareil Cast ou s'en déconnecter depuis n'importe quelle activité de votre application, comme décrit dans la checklist de conception Google Cast.
Dépendances
Mettez à jour le fichier app/build.gradle pour inclure les dépendances de la bibliothèque nécessaires :
dependencies {
implementation 'androidx.appcompat:appcompat:1.5.0'
implementation 'androidx.mediarouter:mediarouter:1.3.1'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'com.google.android.gms:play-services-cast-framework:21.1.0'
implementation 'com.android.volley:volley:1.2.1'
implementation "androidx.core:core-ktx:1.8.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
Synchronisez le projet pour vérifier qu'il ne comporte aucune erreur.
Initialisation
Le framework Cast dispose d'un objet singleton global, le CastContext
, qui coordonne toutes les interactions Cast.
Vous devez implémenter l'interface OptionsProvider
pour fournir les CastOptions
nécessaires à l'initialisation du singleton CastContext
. L'option la plus importante est l'ID de l'application "récepteur" : elle permet de filtrer les résultats lors de la détection des appareils Cast et de lancer l'application "récepteur" lorsqu'une session Cast est démarrée.
Lorsque vous développez votre propre application compatible Cast, vous devez vous inscrire en tant que développeur Cast afin d'obtenir votre ID d'application. Cela dit, dans cet atelier de programmation, nous utiliserons un exemple d'ID d'application.
Ajoutez le nouveau fichier CastOptionsProvider.kt
suivant au package com.google.sample.cast.refplayer
du projet:
package com.google.sample.cast.refplayer
import android.content.Context
import com.google.android.gms.cast.framework.OptionsProvider
import com.google.android.gms.cast.framework.CastOptions
import com.google.android.gms.cast.framework.SessionProvider
class CastOptionsProvider : OptionsProvider {
override fun getCastOptions(context: Context): CastOptions {
return CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.build()
}
override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
return null
}
}
Déclarez ensuite l'OptionsProvider
dans le tag application
du fichier AndroidManifest.xml
:
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.sample.cast.refplayer.CastOptionsProvider" />
Dans VideoBrowserActivity
, appelez la méthode onCreate pour lancer en douceur l'initialisation de CastContext
:
import com.google.android.gms.cast.framework.CastContext
private var mCastContext: CastContext? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.video_browser)
setupActionBar()
mCastContext = CastContext.getSharedInstance(this)
}
Appliquez la même logique d'initialisation à LocalPlayerActivity
.
Icône Cast
Après avoir initialisé CastContext
, nous devons ajouter l'icône Cast pour permettre à l'utilisateur de choisir son appareil Cast. L'icône Cast est implémentée par le MediaRouteButton
de la bibliothèque Support MediaRouter. Comme pour toute icône d'action que vous pouvez ajouter à votre activité via une ActionBar
(barre d'action) ou une Toolbar
(barre d'outils), vous devez d'abord ajouter l'élément de menu correspondant à votre menu.
Pour ce faire, modifiez le fichier res/menu/browse.xml
et ajoutez l'élément MediaRouteActionProvider
dans le menu avant l'élément des paramètres:
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always"/>
Dans VideoBrowserActivity
, remplacez la méthode onCreateOptionsMenu()
par CastButtonFactory
pour implémenter le MediaRouteButton
dans le framework Cast:
import com.google.android.gms.cast.framework.CastButtonFactory
private var mediaRouteMenuItem: MenuItem? = null
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.browse, menu)
mediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu,
R.id.media_route_menu_item)
return true
}
Procédez de la même manière pour remplacer onCreateOptionsMenu
dans LocalPlayerActivity
.
Cliquez sur le bouton Run (Exécuter) pour exécuter l'application sur votre appareil mobile. Une icône Cast devrait maintenant apparaître dans la barre d'action de l'application. Elle affichera la liste des appareils Cast de votre réseau local si vous cliquez dessus. La détection des appareils est automatiquement gérée par CastContext
. Sélectionnez ensuite votre appareil Cast pour y charger l'exemple de l'application récepteur. Sachez que le fait de passer de l'activité de navigation à l'activité de lecture en local n'affecte pas l'état de votre icône Cast qui restera synchronisée.
Pour le moment, vous ne pouvez pas lire de vidéos sur l'appareil Cast, car nous n'avons pas encore connecté de support média. Cliquez sur l'icône Cast pour vous déconnecter.
6. Diffuser du contenu vidéo
Nous allons étendre notre exemple d'application à la lecture de vidéos à distance sur un appareil Cast. Pour ce faire, nous devons écouter les différents événements générés par le framework Cast.
Caster un contenu multimédia
En règle générale, si vous souhaitez lire un contenu multimédia sur un appareil Cast, vous devez procéder comme suit :
- Créez un objet
MediaInfo
qui modélise un élément multimédia. - Connectez-vous à l'appareil Cast, puis lancez votre application "récepteur".
- Chargez l'objet
MediaInfo
sur votre récepteur et lisez son contenu. - Suivez l'état du contenu multimédia.
- Envoyez des commandes de lecture au récepteur en fonction des interactions de l'utilisateur.
Nous avons déjà effectué l'étape 2 à la section précédente. L'étape 3 est simple à réaliser avec le framework Cast. Quant à l'étape 1, elle équivaut au mappage d'un objet sur un autre : MediaInfo
étant quelque chose que le framework Cast comprend, et MediaItem
étant l'encapsulation de notre application pour un élément multimédia, nous devrions facilement pouvoir mapper un MediaItem
sur un MediaInfo
.
L'exemple d'application LocalPlayerActivity
fait déjà la distinction entre la lecture en local et la lecture à distance à l'aide de l'énumération suivante :
private var mLocation: PlaybackLocation? = null
enum class PlaybackLocation {
LOCAL, REMOTE
}
enum class PlaybackState {
PLAYING, PAUSED, BUFFERING, IDLE
}
Dans cet atelier de programmation, vous n'avez pas besoin de comprendre exactement comment fonctionne la logique du lecteur de test. En revanche, il est important que vous compreniez que le lecteur multimédia de votre application doit être modifié de sorte à pouvoir également détecter ces deux contextes de lecture.
Pour le moment, notre lecteur local est toujours configuré à l'état de lecture locale puisque la possibilité de caster du contenu lui est encore inconnue. Nous devons donc mettre à jour l'interface utilisateur pour prendre en compte les transitions d'état qui se produisent dans le framework Cast. Par exemple, si nous commençons à caster un contenu, nous devons arrêter la lecture en local et désactiver certaines commandes. De même, si nous arrêtons de caster un contenu, nous devons repasser en lecture locale. Pour cela, il nous faut écouter les différents événements générés par le framework Cast.
Gestion d'une session Cast
Dans le framework Cast, une session Cast combine plusieurs étapes : la connexion à un appareil, le lancement (ou la reprise) d'une session, la connexion à une application "récepteur" et l'initialisation d'un canal de commande multimédia, le cas échéant. Le canal de commande multimédia détermine la façon dont le framework Cast envoie et reçoit les messages du lecteur multimédia récepteur.
La session Cast démarre automatiquement lorsque l'utilisateur sélectionne un appareil à partir de l'icône Cast, puis s'arrête automatiquement lorsque l'utilisateur se déconnecte. La reconnexion à une session sur un appareil récepteur suite à des problèmes de réseau est aussi gérée automatiquement par le SDK Cast.
Ajoutons un écouteur SessionManagerListener
à l'activité LocalPlayerActivity
:
import com.google.android.gms.cast.framework.CastSession
import com.google.android.gms.cast.framework.SessionManagerListener
...
private var mSessionManagerListener: SessionManagerListener<CastSession>? = null
private var mCastSession: CastSession? = null
...
private fun setupCastListener() {
mSessionManagerListener = object : SessionManagerListener<CastSession> {
override fun onSessionEnded(session: CastSession, error: Int) {
onApplicationDisconnected()
}
override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) {
onApplicationConnected(session)
}
override fun onSessionResumeFailed(session: CastSession, error: Int) {
onApplicationDisconnected()
}
override fun onSessionStarted(session: CastSession, sessionId: String) {
onApplicationConnected(session)
}
override fun onSessionStartFailed(session: CastSession, error: Int) {
onApplicationDisconnected()
}
override fun onSessionStarting(session: CastSession) {}
override fun onSessionEnding(session: CastSession) {}
override fun onSessionResuming(session: CastSession, sessionId: String) {}
override fun onSessionSuspended(session: CastSession, reason: Int) {}
private fun onApplicationConnected(castSession: CastSession) {
mCastSession = castSession
if (null != mSelectedMedia) {
if (mPlaybackState == PlaybackState.PLAYING) {
mVideoView!!.pause()
loadRemoteMedia(mSeekbar!!.progress, true)
return
} else {
mPlaybackState = PlaybackState.IDLE
updatePlaybackLocation(PlaybackLocation.REMOTE)
}
}
updatePlayButton(mPlaybackState)
invalidateOptionsMenu()
}
private fun onApplicationDisconnected() {
updatePlaybackLocation(PlaybackLocation.LOCAL)
mPlaybackState = PlaybackState.IDLE
mLocation = PlaybackLocation.LOCAL
updatePlayButton(mPlaybackState)
invalidateOptionsMenu()
}
}
}
Ce qui nous intéresse dans l'activité LocalPlayerActivity
, c'est d'être informés en cas de connexion ou de déconnexion de l'appareil Cast afin de pouvoir basculer vers le lecteur local si nécessaire, et inversement. Notez que la connectivité peut être interrompue par l'instance de votre application s'exécutant sur votre appareil mobile, mais également par une autre instance de votre application (ou d'une autre application) exécutée sur un autre appareil mobile.
La session actuellement active est accessible en tant que SessionManager.getCurrentSession()
. Les sessions sont créées et détruites automatiquement en réponse aux interactions de l'utilisateur avec les boîtes de dialogue Cast.
Nous allons maintenant enregistrer notre écouteur de session et initialiser certaines variables que nous utiliserons dans l'activité. Remplacez la méthode onCreate
de LocalPlayerActivity
par :
import com.google.android.gms.cast.framework.CastContext
...
private var mCastContext: CastContext? = null
...
override fun onCreate(savedInstanceState: Bundle?) {
...
mCastContext = CastContext.getSharedInstance(this)
mCastSession = mCastContext!!.sessionManager.currentCastSession
setupCastListener()
...
loadViews()
...
val bundle = intent.extras
if (bundle != null) {
....
if (shouldStartPlayback) {
....
} else {
if (mCastSession != null && mCastSession!!.isConnected()) {
updatePlaybackLocation(PlaybackLocation.REMOTE)
} else {
updatePlaybackLocation(PlaybackLocation.LOCAL)
}
mPlaybackState = PlaybackState.IDLE
updatePlayButton(mPlaybackState)
}
}
...
}
Chargement de contenu multimédia
Dans le SDK Cast, RemoteMediaClient
fournit un ensemble d'API pratiques pour gérer la lecture de contenus multimédias à distance sur le récepteur. Pour toute CastSession
compatible avec la lecture de contenus multimédias, une instance de RemoteMediaClient
sera créée automatiquement par le SDK. Vous pouvez y accéder en appelant la méthode getRemoteMediaClient()
dans l'instance CastSession
. Ajoutez les méthodes suivantes à LocalPlayerActivity
pour charger la vidéo actuellement sélectionnée sur le récepteur :
import com.google.android.gms.cast.framework.media.RemoteMediaClient
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaLoadOptions
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.common.images.WebImage
import com.google.android.gms.cast.MediaLoadRequestData
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
if (mCastSession == null) {
return
}
val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return
remoteMediaClient.load( MediaLoadRequestData.Builder()
.setMediaInfo(buildMediaInfo())
.setAutoplay(autoPlay)
.setCurrentTime(position.toLong()).build())
}
private fun buildMediaInfo(): MediaInfo? {
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
mSelectedMedia?.studio?.let { movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, it) }
mSelectedMedia?.title?.let { movieMetadata.putString(MediaMetadata.KEY_TITLE, it) }
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(0))))
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(1))))
return mSelectedMedia!!.url?.let {
MediaInfo.Builder(it)
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType("videos/mp4")
.setMetadata(movieMetadata)
.setStreamDuration((mSelectedMedia!!.duration * 1000).toLong())
.build()
}
}
À présent, mettez à jour les méthodes existantes ci-dessous pour que la logique de la session Cast permette la lecture à distance :
private fun play(position: Int) {
startControllersTimer()
when (mLocation) {
PlaybackLocation.LOCAL -> {
mVideoView!!.seekTo(position)
mVideoView!!.start()
}
PlaybackLocation.REMOTE -> {
mPlaybackState = PlaybackState.BUFFERING
updatePlayButton(mPlaybackState)
//seek to a new position within the current media item's new position
//which is in milliseconds from the beginning of the stream
mCastSession!!.remoteMediaClient?.seek(position.toLong())
}
else -> {}
}
restartTrickplayTimer()
}
private fun togglePlayback() {
...
PlaybackState.IDLE -> when (mLocation) {
...
PlaybackLocation.REMOTE -> {
if (mCastSession != null && mCastSession!!.isConnected) {
loadRemoteMedia(mSeekbar!!.progress, true)
}
}
else -> {}
}
...
}
override fun onPause() {
...
mCastContext!!.sessionManager.removeSessionManagerListener(
mSessionManagerListener!!, CastSession::class.java)
}
override fun onResume() {
Log.d(TAG, "onResume() was called")
mCastContext!!.sessionManager.addSessionManagerListener(
mSessionManagerListener!!, CastSession::class.java)
if (mCastSession != null && mCastSession!!.isConnected) {
updatePlaybackLocation(PlaybackLocation.REMOTE)
} else {
updatePlaybackLocation(PlaybackLocation.LOCAL)
}
super.onResume()
}
Dans le cas de la méthode updatePlayButton
, modifiez la valeur de la variable isConnected
:
private fun updatePlayButton(state: PlaybackState?) {
...
val isConnected = (mCastSession != null
&& (mCastSession!!.isConnected || mCastSession!!.isConnecting))
...
}
Cliquez sur le bouton Run (Exécuter) pour exécuter l'application sur votre appareil mobile. Ensuite, connectez-vous à votre appareil Cast et lancez la lecture d'une vidéo. Votre session Cast devrait maintenant commencer sur le récepteur.
7. Mini-télécommande
La checklist de conception de Google Cast exige que toutes les applications Cast soient dotées d'une mini-télécommande qui s'affiche dès que l'utilisateur quitte la page de contenu actuelle. Cette mini-télécommande permet à l'utilisateur d'accéder instantanément à sa session Cast en cours et de disposer d'un rappel visible.
Le SDK Cast fournit une vue personnalisée, MiniControllerFragment
, qui peut être ajoutée au fichier de mise en page des activités pour lesquelles vous souhaitez afficher la mini-télécommande.
Ajoutez la définition de fragment suivante en bas de res/layout/player_activity.xml
et res/layout/video_browser.xml
:
<fragment
android:id="@+id/castMiniController"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment"/>
Cliquez sur le bouton Run (Exécuter) pour exécuter l'application et caster une vidéo. Au démarrage de la lecture sur le récepteur, vous devriez maintenant voir s'afficher la mini-télécommande en bas de chaque activité. Vous pouvez contrôler la lecture à distance à l'aide de la mini-télécommande. Si vous passez de l'activité de navigation à l'activité de lecture en local, l'état de votre mini-télécommande restera synchronisé avec le statut du lecteur multimédia récepteur.
8. Notifications et écran de verrouillage
La checklist de conception de Google Cast exige que les applications "émetteur" implémentent des commandes multimédias via la barre de notifications et l'écran de verrouillage.
Afin d'aider l'application "émetteur" à créer des commandes multimédias via les notifications et l'écran de verrouillage, le SDK Cast fournit un service de notification multimédia : MediaNotificationService
. Ce service est automatiquement fusionné avec le fichier manifeste de votre application par Gradle.
Ainsi, lorsque l'émetteur caste un contenu, MediaNotificationService
s'exécute en arrière-plan et affiche une notification qui contient une vignette d'image et des métadonnées sur l'élément en cours de diffusion, ainsi qu'un bouton de lecture/pause et un bouton pour arrêter la lecture.
Les commandes accessibles au niveau de la notification et de l'écran de verrouillage peuvent être activées avec CastOptions
lors de l'initialisation de CastContext
. Elles sont activées par défaut. Tant que la fonctionnalité de notification restera activée, la fonctionnalité de l'écran de verrouillage le sera également.
Modifiez CastOptionsProvider
et changez l'implémentation de getCastOptions
de façon à obtenir le code suivant :
import com.google.android.gms.cast.framework.media.CastMediaOptions
import com.google.android.gms.cast.framework.media.NotificationOptions
override fun getCastOptions(context: Context): CastOptions {
val notificationOptions = NotificationOptions.Builder()
.setTargetActivityClassName(VideoBrowserActivity::class.java.name)
.build()
val mediaOptions = CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
.build()
return CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.setCastMediaOptions(mediaOptions)
.build()
}
Cliquez sur le bouton Run (Exécuter) pour exécuter l'application sur votre appareil mobile. Castez une vidéo et quittez l'exemple d'application. Une notification concernant la vidéo en cours de lecture sur le récepteur devrait maintenant apparaître. Verrouillez votre appareil mobile. L'écran de verrouillage devrait à présent afficher les commandes de lecture multimédia sur l'appareil Cast.
9. Message d'annonce en superposition
La checklist de conception de Google Cast exige que l'application émettrice présente l'icône Cast aux utilisateurs existants pour les prévenir que cette nouvelle fonctionnalité est disponible et aider ceux qui ne connaissent pas encore Google Cast.
Le SDK Cast fournit une vue personnalisée, IntroductoryOverlay
, qui permet de mettre en surbrillance l'icône Cast lorsqu'elle s'affiche pour la première fois auprès des utilisateurs. Ajoutez le code suivant à VideoBrowserActivity
:
import com.google.android.gms.cast.framework.IntroductoryOverlay
import android.os.Looper
private var mIntroductoryOverlay: IntroductoryOverlay? = null
private fun showIntroductoryOverlay() {
mIntroductoryOverlay?.remove()
if (mediaRouteMenuItem?.isVisible == true) {
Looper.myLooper().run {
mIntroductoryOverlay = com.google.android.gms.cast.framework.IntroductoryOverlay.Builder(
this@VideoBrowserActivity, mediaRouteMenuItem!!)
.setTitleText("Introducing Cast")
.setSingleTime()
.setOnOverlayDismissedListener(
object : IntroductoryOverlay.OnOverlayDismissedListener {
override fun onOverlayDismissed() {
mIntroductoryOverlay = null
}
})
.build()
mIntroductoryOverlay!!.show()
}
}
}
Ajoutez maintenant un écouteur CastStateListener
et appelez la méthode showIntroductoryOverlay
lorsqu'un appareil Cast est disponible en modifiant la méthode onCreate
. Remplacez ensuite les méthodes onResume
et onPause
pour qu'elles correspondent à ce qui suit:
import com.google.android.gms.cast.framework.CastState
import com.google.android.gms.cast.framework.CastStateListener
private var mCastStateListener: CastStateListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.video_browser)
setupActionBar()
mCastStateListener = object : CastStateListener {
override fun onCastStateChanged(newState: Int) {
if (newState != CastState.NO_DEVICES_AVAILABLE) {
showIntroductoryOverlay()
}
}
}
mCastContext = CastContext.getSharedInstance(this)
}
override fun onResume() {
super.onResume()
mCastContext?.addCastStateListener(mCastStateListener!!)
}
override fun onPause() {
super.onPause()
mCastContext?.removeCastStateListener(mCastStateListener!!)
}
Effacez les données de l'application ou supprimez-la de votre appareil. Cliquez ensuite sur le bouton Run (Exécuter) pour exécuter l'application sur votre appareil mobile. La superposition d'introduction doit s'afficher. Si la superposition ne s'affiche pas, effacez les données de l'application.
10. Télécommande agrandie
La checklist de conception de Google Cast exige que l'application émettrice soit dotée d'une télécommande agrandie associée au contenu multimédia en cours de diffusion. La télécommande agrandie est une version en plein écran de la mini-télécommande.
Le SDK Cast fournit un widget pour la télécommande agrandie appelé ExpandedControllerActivity
. Il s'agit d'une classe abstraite que vous devez sous-classer pour ajouter une icône Cast.
Commencez par créer un fichier de ressources de menu appelé expanded_controller.xml
afin d'intégrer l'icône Cast à la télécommande agrandie :
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always"/>
</menu>
Créez un package expandedcontrols
dans le package com.google.sample.cast.refplayer
. Ensuite, créez un fichier nommé ExpandedControlsActivity.kt
dans le package com.google.sample.cast.refplayer.expandedcontrols
.
package com.google.sample.cast.refplayer.expandedcontrols
import android.view.Menu
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
import com.google.sample.cast.refplayer.R
import com.google.android.gms.cast.framework.CastButtonFactory
class ExpandedControlsActivity : ExpandedControllerActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.expanded_controller, menu)
CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
return true
}
}
Déclarez ensuite l'ExpandedControlsActivity
dans le AndroidManifest.xml
au sein de la balise application
au-dessus de OPTIONS_PROVIDER_CLASS_NAME
:
<application>
...
<activity
android:name="com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/Theme.CastVideosDark"
android:screenOrientation="portrait"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.google.sample.cast.refplayer.VideoBrowserActivity"/>
</activity>
...
</application>
Modifiez CastOptionsProvider
, puis changez NotificationOptions
et CastMediaOptions
de façon à définir l'activité cible sur ExpandedControlsActivity
:
import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity
override fun getCastOptions(context: Context): CastOptions {
val notificationOptions = NotificationOptions.Builder()
.setTargetActivityClassName(ExpandedControlsActivity::class.java.name)
.build()
val mediaOptions = CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
.setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name)
.build()
return CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.setCastMediaOptions(mediaOptions)
.build()
}
Mettez à jour la méthode loadRemoteMedia
de LocalPlayerActivity
afin d'afficher ExpandedControlsActivity
lorsque le chargement du contenu multimédia à distance est terminé :
import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
if (mCastSession == null) {
return
}
val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return
remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() {
override fun onStatusUpdated() {
val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java)
startActivity(intent)
remoteMediaClient.unregisterCallback(this)
}
})
remoteMediaClient.load(MediaLoadRequestData.Builder()
.setMediaInfo(buildMediaInfo())
.setAutoplay(autoPlay)
.setCurrentTime(position.toLong()).build())
}
Cliquez sur le bouton Run (Exécuter) pour exécuter l'application sur votre appareil mobile et caster une vidéo. Vous devriez maintenant voir s'afficher la télécommande agrandie. Revenez à la liste des vidéos, puis cliquez sur la mini-télécommande pour l'agrandir et l'afficher à nouveau en plein écran. Quittez l'application pour voir la notification. Cliquez sur l'image de la notification pour charger la télécommande agrandie.
11. Ajouter la compatibilité avec Cast Connect
La bibliothèque Cast Connect permet aux applications émettrices existantes de communiquer avec les applications Android TV via le protocole Cast. Cast Connect s'appuie sur l'infrastructure Cast, et votre application Android TV joue le rôle de récepteur.
Dépendances
Remarque: Pour implémenter Cast Connect, play-services-cast-framework
doit être 19.0.0
ou une version ultérieure.
LaunchOptions
Pour lancer l'application Android TV, également appelée Android Receiver, nous devons définir l'indicateur setAndroidReceiverCompatible
sur "true" dans l'objet LaunchOptions
. Cet objet LaunchOptions
détermine comment le récepteur est lancé et est transmis à l'CastOptions
renvoyé par la classe CastOptionsProvider
. Si vous définissez l'indicateur mentionné ci-dessus sur false
, le récepteur Web pour l'ID d'application défini dans la console développeur Cast sera lancé.
Dans le fichier CastOptionsProvider.kt
, ajoutez ce qui suit à la méthode getCastOptions
:
import com.google.android.gms.cast.LaunchOptions
...
val launchOptions = LaunchOptions.Builder()
.setAndroidReceiverCompatible(true)
.build()
return new CastOptions.Builder()
.setLaunchOptions(launchOptions)
...
.build()
Définir les identifiants de lancement
Du côté de l'expéditeur, vous pouvez spécifier CredentialsData
pour représenter la personne qui rejoint la session. credentials
est une chaîne pouvant être définie par l'utilisateur, à condition que votre application ATV puisse la comprendre. Le CredentialsData
n'est transmis à votre application Android TV que pendant le lancement ou l'heure de connexion. Si vous le définissez à nouveau lorsque vous êtes connecté, il ne sera pas transmis à votre application Android TV.
Pour définir les identifiants de lancement, CredentialsData
doit être défini et transmis à l'objet LaunchOptions
. Ajoutez le code suivant à la méthode getCastOptions
dans votre fichier CastOptionsProvider.kt
:
import com.google.android.gms.cast.CredentialsData
...
val credentialsData = CredentialsData.Builder()
.setCredentials("{\"userId\": \"abc\"}")
.build()
val launchOptions = LaunchOptions.Builder()
...
.setCredentialsData(credentialsData)
.build()
Définir des identifiants sur LoadRequest
Si votre application Web Receiver et votre application Android TV gèrent credentials
différemment, vous devrez peut-être définir des credentials
distincts pour chacune. Pour ce faire, ajoutez le code suivant dans votre fichier LocalPlayerActivity.kt
sous la fonction loadRemoteMedia
:
remoteMediaClient.load(MediaLoadRequestData.Builder()
...
.setCredentials("user-credentials")
.setAtvCredentials("atv-user-credentials")
.build())
En fonction de l'application réceptrice sur laquelle l'émetteur diffuse du contenu, le SDK gère désormais automatiquement les identifiants à utiliser pour la session en cours.
Test de Cast Connect
Procédure d'installation de l'APK Android TV sur Chromecast avec Google TV
- Recherchez l'adresse IP de votre appareil Android TV. Elle est généralement disponible sous Paramètres > Réseau et Internet > (Nom du réseau auquel votre appareil est connecté). À droite, vous trouverez les informations et l'adresse IP de votre appareil sur le réseau.
- Utilisez l'adresse IP de votre appareil pour vous y connecter via ADB à l'aide du terminal:
$ adb connect <device_ip_address>:5555
- Dans la fenêtre de votre terminal, accédez au dossier de premier niveau contenant les exemples de l'atelier de programmation que vous avez téléchargés au début de cet atelier de programmation. Exemple :
$ cd Desktop/android_codelab_src
- Installez le fichier .apk de ce dossier sur votre Android TV en exécutant la commande suivante:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- Vous devriez à présent voir une application nommée Caster des vidéos dans le menu Vos applications de votre appareil Android TV.
- Revenez à votre projet Android Studio, puis cliquez sur le bouton "Run" (Exécuter) pour installer et exécuter l'application d'envoi sur votre appareil mobile physique. En haut à droite, cliquez sur l'icône Cast et sélectionnez votre appareil Android TV parmi les options disponibles. L'application Android TV devrait maintenant s'ouvrir sur votre appareil Android TV. Si vous lancez une vidéo, vous devriez pouvoir la contrôler à l'aide de votre télécommande Android TV.
12. Personnaliser les widgets Cast
Vous pouvez personnaliser les widgets Cast en définissant les couleurs, le type et le style des boutons à afficher et du texte, ainsi que l'apparence des vignettes.
Mettre à jour res/values/styles_castvideo.xml
<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="mediaRouteTheme">@style/CustomMediaRouterTheme</item>
<item name="castIntroOverlayStyle">@style/CustomCastIntroOverlay</item>
<item name="castMiniControllerStyle">@style/CustomCastMiniController</item>
<item name="castExpandedControllerStyle">@style/CustomCastExpandedController</item>
<item name="castExpandedControllerToolbarStyle">
@style/ThemeOverlay.AppCompat.ActionBar
</item>
...
</style>
Déclarez les thèmes personnalisés suivants :
<!-- Customize Cast Button -->
<style name="CustomMediaRouterTheme" parent="Theme.MediaRouter">
<item name="mediaRouteButtonStyle">@style/CustomMediaRouteButtonStyle</item>
</style>
<style name="CustomMediaRouteButtonStyle" parent="Widget.MediaRouter.Light.MediaRouteButton">
<item name="mediaRouteButtonTint">#EEFF41</item>
</style>
<!-- Customize Introductory Overlay -->
<style name="CustomCastIntroOverlay" parent="CastIntroOverlay">
<item name="castButtonTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Button</item>
<item name="castTitleTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Title</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Button" parent="android:style/TextAppearance">
<item name="android:textColor">#FFFFFF</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Title" parent="android:style/TextAppearance.Large">
<item name="android:textColor">#FFFFFF</item>
</style>
<!-- Customize Mini Controller -->
<style name="CustomCastMiniController" parent="CastMiniController">
<item name="castShowImageThumbnail">true</item>
<item name="castTitleTextAppearance">@style/TextAppearance.AppCompat.Subhead</item>
<item name="castSubtitleTextAppearance">@style/TextAppearance.AppCompat.Caption</item>
<item name="castBackground">@color/accent</item>
<item name="castProgressBarColor">@color/orange</item>
</style>
<!-- Customize Expanded Controller -->
<style name="CustomCastExpandedController" parent="CastExpandedController">
<item name="castButtonColor">#FFFFFF</item>
<item name="castPlayButtonDrawable">@drawable/cast_ic_expanded_controller_play</item>
<item name="castPauseButtonDrawable">@drawable/cast_ic_expanded_controller_pause</item>
<item name="castStopButtonDrawable">@drawable/cast_ic_expanded_controller_stop</item>
</style>
13. Félicitations
Vous savez désormais comment rendre une application vidéo compatible avec Cast à l'aide des widgets du SDK Cast sur Android.
Pour en savoir plus, consultez le guide du développeur Android Sender.