Ce guide du développeur explique comment ajouter la compatibilité avec Google Cast à votre application émettrice Android à l'aide du SDK émetteur Android.
L'appareil mobile ou l'ordinateur portable est l'émetteur qui contrôle la lecture, et l'appareil Google Cast est le récepteur qui affiche le contenu sur le téléviseur.
Le framework émetteur fait référence au binaire de la bibliothèque de classes Cast et aux ressources associées présentes au moment de l'exécution sur l'émetteur. L'application émettrice ou application Cast fait référence à une application qui s'exécute également sur l'émetteur. L'application réceptrice Web fait référence à l'application HTML qui s'exécute sur l'appareil compatible Cast.
Le framework émetteur utilise une conception de rappel asynchrone pour informer l'application émettrice des événements et passer d'un état à l'autre du cycle de vie de l'application Cast.
Déroulement des opérations de l'application
Les étapes suivantes décrivent le flux d'exécution général typique d'une application Android émettrice :
- Le framework Cast démarre automatiquement la détection des appareils
MediaRouteren fonction du cycle de vieActivity. - Lorsque l'utilisateur clique sur l'icône Cast, le framework affiche la boîte de dialogue Cast avec la liste des appareils Cast détectés.
- Lorsque l'utilisateur sélectionne un appareil Cast, le framework tente de lancer l'application réceptrice Web sur l'appareil Cast.
- Le framework appelle des rappels dans l'application émettrice pour confirmer que l'application réceptrice Web a été lancée.
- Le framework crée un canal de communication entre les applications émettrice et réceptrice Web.
- Le framework utilise le canal de communication pour charger et contrôler la lecture multimédia sur le récepteur Web.
- Le framework synchronise l'état de la lecture multimédia entre l'émetteur et le récepteur Web : lorsque l'utilisateur effectue des actions dans l'UI de l'émetteur, le framework transmet ces requêtes de contrôle multimédia au récepteur Web. Lorsque le récepteur Web envoie des mises à jour de l'état du contenu multimédia, le framework met à jour l'état de l'UI de l'émetteur.
- Lorsque l'utilisateur clique sur l'icône Cast pour se déconnecter de l'appareil Cast, le framework déconnecte l'application émettrice du récepteur Web.
Pour obtenir une liste complète de toutes les classes, méthodes et événements du SDK Android Google Cast, consultez la documentation de référence de l'API émettrice Google Cast pour Android. Les sections suivantes décrivent les étapes à suivre pour ajouter Cast à votre application Android.
Configurer le fichier manifeste Android
Le fichier AndroidManifest.xml de votre application vous oblige à configurer les éléments suivants pour le SDK Cast :
uses-sdk
Définissez les niveaux d'API Android minimal et cible compatibles avec le SDK Cast. Actuellement, le niveau minimal est l'API 23 et le niveau cible est l'API 34.
<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="34" />
android:theme
Définissez le thème de votre application en fonction de la version minimale du SDK Android. Par exemple, si vous n'implémentez pas votre propre thème, vous devez utiliser une variante de Theme.AppCompat lorsque vous ciblez une version minimale du SDK Android antérieure à Lollipop.
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat" >
...
</application>
Initialiser le contexte Cast
Le framework possède un objet Singleton global, le CastContext, qui coordonne toutes les interactions du framework.
Votre application doit implémenter l'
OptionsProvider
interface pour fournir les options nécessaires à l'initialisation du
CastContext
singleton. OptionsProvider fournit une instance de
CastOptions
qui contient des options affectant le comportement du framework. La plus importante de ces options est l'ID d'application du récepteur Web, utilisé pour filtrer les résultats de la détection et lancer l'application réceptrice Web lorsqu'une session Cast est lancée.
class CastOptionsProvider : OptionsProvider { override fun getCastOptions(context: Context): CastOptions { return Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .build() } override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? { return null } }
public class CastOptionsProvider implements OptionsProvider { @Override public CastOptions getCastOptions(Context context) { CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .build(); return castOptions; } @Override public List<SessionProvider> getAdditionalSessionProviders(Context context) { return null; } }
Vous devez déclarer le nom complet de l'élément OptionsProvider implémenté en tant que champ de métadonnées dans le fichier AndroidManifest.xml de l'application émettrice :
<application>
...
<meta-data
android:name=
"com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.foo.CastOptionsProvider" />
</application>
CastContext est initialisé de manière différée lorsque CastContext.getSharedInstance()
est appelé.
class MyActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { val castContext = CastContext.getSharedInstance(this) } }
public class MyActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { CastContext castContext = CastContext.getSharedInstance(this); } }
Widgets d'UX Cast
Le framework Cast fournit les widgets conformes à la checklist de conception Cast :
Superposition d'introduction : le framework fournit une vue personnalisée,
IntroductoryOverlay, qui s'affiche pour l'utilisateur afin d'attirer son attention sur l'icône Cast la première fois qu'un récepteur est disponible. L'application émettrice peut personnaliser le texte et la position du texte du titre.Icône Cast: l'icône Cast est visible, quelle que soit la disponibilité des appareils Cast. Lorsque l'utilisateur clique sur l'icône Cast pour la première fois, une boîte de dialogue Cast s'affiche et répertorie les appareils détectés. Lorsque l'utilisateur clique sur l'icône Cast alors que l'appareil est connecté, les métadonnées multimédias actuelles (telles que le titre, le nom du studio d'enregistrement et une image miniature) s'affichent ou l'utilisateur peut se déconnecter de l'appareil Cast. L'icône Cast est parfois appelée "icône Cast".
Mini-télécommande: lorsque l'utilisateur caste du contenu et quitte la page de contenu actuelle ou la télécommande agrandie pour accéder à un autre écran de l'application émettrice, la mini-télécommande s'affiche en bas de l'écran pour lui permettre de voir les métadonnées multimédias en cours de diffusion et de contrôler la lecture.
Télécommande agrandie : lorsque l'utilisateur caste du contenu, s'il clique sur la notification multimédia ou la mini-télécommande, la télécommande agrandie s'affiche. Elle affiche les métadonnées multimédias en cours de lecture et fournit plusieurs boutons pour contrôler la lecture multimédia.
Notification : Android uniquement. Lorsque l'utilisateur caste du contenu et quitte l'application émettrice, une notification multimédia s'affiche. Elle indique les métadonnées multimédias en cours de diffusion et les commandes de lecture.
Écran de verrouillage: Android uniquement. Lorsque l'utilisateur caste du contenu et accède à l'écran de verrouillage (ou que l'appareil se met en veille), une commande multimédia s'affiche. Elle indique les métadonnées multimédias en cours de diffusion et les commandes de lecture.
Le guide suivant explique comment ajouter ces widgets à votre application.
Ajouter une icône Cast
Les API Android
MediaRouter
sont conçues pour permettre l'affichage et la lecture de contenus multimédias sur des appareils secondaires.
Les applications Android qui utilisent l'API MediaRouter doivent inclure une icône Cast dans leur interface utilisateur pour permettre aux utilisateurs de sélectionner un itinéraire multimédia afin de lire du contenu multimédia sur un appareil secondaire, tel qu'un appareil Cast.
Le framework facilite l'ajout d'un
MediaRouteButton
en tant qu'
Cast button
très facile. Vous devez d'abord ajouter un élément de menu ou un MediaRouteButton dans le fichier XML
qui définit votre menu, puis utiliser
CastButtonFactory
pour le connecter au framework.
// To add a Cast button, add the following snippet.
// menu.xml
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always" />
// Then override the onCreateOptionMenu() for each of your activities. // MyActivity.kt override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.main, menu) CastButtonFactory.setUpMediaRouteButton( applicationContext, menu, R.id.media_route_menu_item ) return true }
// Then override the onCreateOptionMenu() for each of your activities. // MyActivity.java @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.main, menu); CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu, R.id.media_route_menu_item); return true; }
Ensuite, si votre Activity hérite de
FragmentActivity,
vous pouvez ajouter un
MediaRouteButton
à votre mise en page.
// activity_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<androidx.mediarouter.app.MediaRouteButton
android:id="@+id/media_route_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:mediaRouteTypes="user"
android:visibility="gone" />
</LinearLayout>
// MyActivity.kt override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_layout) mMediaRouteButton = findViewById<View>(R.id.media_route_button) as MediaRouteButton CastButtonFactory.setUpMediaRouteButton(applicationContext, mMediaRouteButton) mCastContext = CastContext.getSharedInstance(this) }
// MyActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_layout); mMediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button); CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), mMediaRouteButton); mCastContext = CastContext.getSharedInstance(this); }
Pour définir l'apparence de l'icône Cast à l'aide d'un thème, consultez Personnaliser l'icône Cast.
Configurer la détection des appareils
La détection des appareils est entièrement gérée par le
CastContext.
Lors de l'initialisation de CastContext, l'application émettrice spécifie l'ID d'application du récepteur Web
et peut éventuellement demander le filtrage de l'espace de noms en définissant
supportedNamespaces dans
CastOptions.
CastContext contient une référence à MediaRouter en interne et démarre le processus de détection dans les conditions suivantes :
- En fonction d'un algorithme conçu pour équilibrer la latence de détection des appareils et l'utilisation de la batterie, la détection est parfois lancée automatiquement lorsque l'application émettrice passe au premier plan.
- La boîte de dialogue Cast est ouverte.
- Le SDK Cast tente de récupérer une session Cast.
Le processus de détection s'arrête lorsque la boîte de dialogue Cast est fermée ou que l'application émettrice passe en arrière-plan.
class CastOptionsProvider : OptionsProvider { companion object { const val CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace" } override fun getCastOptions(appContext: Context): CastOptions { val supportedNamespaces: MutableList<String> = ArrayList() supportedNamespaces.add(CUSTOM_NAMESPACE) return CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setSupportedNamespaces(supportedNamespaces) .build() } override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? { return null } }
class CastOptionsProvider implements OptionsProvider { public static final String CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace"; @Override public CastOptions getCastOptions(Context appContext) { List<String> supportedNamespaces = new ArrayList<>(); supportedNamespaces.add(CUSTOM_NAMESPACE); CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setSupportedNamespaces(supportedNamespaces) .build(); return castOptions; } @Override public List<SessionProvider> getAdditionalSessionProviders(Context context) { return null; } }
Fonctionnement de la gestion des sessions
Le SDK Cast introduit le concept de session Cast, dont l'établissement combine les étapes de connexion à un appareil, de lancement (ou de reprise) d'une application réceptrice Web, de connexion à cette application et d'initialisation d'un canal de commande multimédia. Pour en savoir plus sur les sessions Cast et le cycle de vie du récepteur Web, consultez le guide sur le cycle de vie de l'application réceptrice Web .
Les sessions sont gérées par la classe
SessionManager,
à laquelle votre application peut accéder via
CastContext.getSessionManager().
Les sessions individuelles sont représentées par des sous-classes de la classe
Session.
Par exemple,
CastSession
représente les sessions avec des appareils Cast. Votre application peut accéder à la session Cast actuellement active
via
SessionManager.getCurrentCastSession().
Votre application peut utiliser la
SessionManagerListener
classe pour surveiller les événements de session, tels que la création, la suspension, la reprise et l'
achèvement. Le framework tente automatiquement de reprendre une session après un arrêt anormal/brutal.
Les sessions sont créées et détruites automatiquement en réponse aux gestes des utilisateurs à partir des boîtes de dialogue MediaRouter.
Pour mieux comprendre les erreurs de démarrage de Cast, les applications peuvent utiliser
CastContext#getCastReasonCodeForCastStatusCode(int)
afin de convertir l'erreur de démarrage de la session en
CastReasonCodes.
Veuillez noter que certaines erreurs de démarrage de session (par exemple, CastReasonCodes#CAST_CANCELLED)
sont un comportement prévu et ne doivent pas être consignées comme des erreurs.
Si vous devez être informé des changements d'état de la session, vous pouvez implémenter un SessionManagerListener. Cet exemple écoute la disponibilité d'une CastSession dans une Activity.
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 inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> { override fun onSessionStarting(session: CastSession?) {} override fun onSessionStarted(session: CastSession?, sessionId: String) { invalidateOptionsMenu() } override fun onSessionStartFailed(session: CastSession?, error: Int) { val castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error) // Handle error } override fun onSessionSuspended(session: CastSession?, reason Int) {} override fun onSessionResuming(session: CastSession?, sessionId: String) {} override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) { invalidateOptionsMenu() } override fun onSessionResumeFailed(session: CastSession?, error: Int) {} override fun onSessionEnding(session: CastSession?) {} override fun onSessionEnded(session: CastSession?, error: Int) { finish() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mCastContext = CastContext.getSharedInstance(this) mSessionManager = mCastContext.sessionManager mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java) } override fun onResume() { super.onResume() mCastSession = mSessionManager.currentCastSession } 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 class SessionManagerListenerImpl implements SessionManagerListener<CastSession> { @Override public void onSessionStarting(CastSession session) {} @Override public void onSessionStarted(CastSession session, String sessionId) { invalidateOptionsMenu(); } @Override public void onSessionStartFailed(CastSession session, int error) { int castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error); // Handle error } @Override public void onSessionSuspended(CastSession session, int reason) {} @Override public void onSessionResuming(CastSession session, String sessionId) {} @Override public void onSessionResumed(CastSession session, boolean wasSuspended) { invalidateOptionsMenu(); } @Override public void onSessionResumeFailed(CastSession session, int error) {} @Override public void onSessionEnding(CastSession session) {} @Override public void onSessionEnded(CastSession session, int error) { finish(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCastContext = CastContext.getSharedInstance(this); mSessionManager = mCastContext.getSessionManager(); mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class); } @Override protected void onResume() { super.onResume(); mCastSession = mSessionManager.getCurrentCastSession(); } @Override protected void onDestroy() { super.onDestroy(); mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class); } }
Transfert de diffusion
La préservation de l'état de la session est à la base du transfert de diffusion, où les utilisateurs peuvent déplacer des flux audio et vidéo existants entre les appareils à l'aide de commandes vocales, de l'application Google Home ou d'écrans connectés. Le contenu multimédia s'arrête sur un appareil (la source) et continue sur un autre (la destination). Tout appareil Cast doté du dernier micrologiciel peut servir de source ou de destination lors d'un transfert de diffusion.
Pour obtenir le nouvel appareil de destination lors d'un transfert ou d'une expansion de diffusion,
enregistrez un
Cast.Listener
à l'aide de
CastSession#addCastListener.
Appelez ensuite
CastSession#getCastDevice()
lors du rappel onDeviceNameChanged.
Pour en savoir plus, consultez Transfert de diffusion sur le récepteur Web.
Reconnexion automatique
Le framework fournit un
ReconnectionService
qui peut être activé par l'application émettrice pour gérer la reconnexion dans de nombreux cas limites
subtils, tels que :
- Récupération après une perte temporaire de connexion Wi-Fi
- Récupération après la mise en veille de l'appareil
- Récupération après la mise en arrière-plan de l'application
- Récupération en cas de plantage de l'application
Ce service est activé par défaut et peut être désactivé dans
CastOptions.Builder.
Ce service peut être automatiquement fusionné avec le fichier manifeste de votre application si la fusion automatique est activée dans votre fichier Gradle.
Le framework démarre le service lorsqu'une session multimédia est en cours et l'arrête lorsque la session multimédia se termine.
Fonctionnement du contrôle multimédia
Le framework Cast déconseille la
RemoteMediaPlayer
classe de Cast 2.x au profit d'une nouvelle classe
RemoteMediaClient,
qui offre les mêmes fonctionnalités dans un ensemble d'API plus pratiques et
évite d'avoir à transmettre un GoogleApiClient.
Lorsque votre application établit une
CastSession
avec une application réceptrice Web compatible avec l'espace de noms multimédia, une instance de
RemoteMediaClient est automatiquement créée par le framework. Votre application peut
y accéder en appelant la méthode getRemoteMediaClient() sur l'instance CastSession
.
Toutes les méthodes de RemoteMediaClient qui émettent des requêtes vers le récepteur Web renvoient un objet PendingResult qui peut être utilisé pour suivre cette requête.
L'instance de RemoteMediaClient peut être partagée par
plusieurs parties de votre application, et même par certains composants internes du
framework, tels que les mini-télécommandes persistantes et
le service de notification.
À cette fin, cette instance est compatible avec l'enregistrement de plusieurs instances de RemoteMediaClient.Listener.
Définir les métadonnées multimédias
La classe représente les informations sur un élément multimédia que vous souhaitez caster.MediaMetadata L'exemple suivant crée une instance MediaMetadata d'un film et définit le titre, le sous-titre et deux images.
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE) movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle()) movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio()) movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(0)))) movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(1))))
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle()); movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio()); movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(0)))); movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1))));
Consultez Sélection d'images sur l'utilisation d'images avec des métadonnées multimédias.
Charger un contenu multimédia
Votre application peut charger un élément multimédia, comme indiqué dans le code suivant. Commencez par utiliser
MediaInfo.Builder
avec les métadonnées du contenu multimédia pour créer une instance
MediaInfo. Obtenez le
RemoteMediaClient
à partir de la CastSession actuelle, puis chargez le MediaInfo dans ce
RemoteMediaClient. Utilisez RemoteMediaClient pour lire, mettre en pause et contrôler une application de lecteur multimédia exécutée sur le récepteur Web.
val mediaInfo = MediaInfo.Builder(mSelectedMedia.getUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("videos/mp4") .setMetadata(movieMetadata) .setStreamDuration(mSelectedMedia.getDuration() * 1000) .build() val remoteMediaClient = mCastSession.getRemoteMediaClient() remoteMediaClient.load(MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build())
MediaInfo mediaInfo = new MediaInfo.Builder(mSelectedMedia.getUrl()) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setContentType("videos/mp4") .setMetadata(movieMetadata) .setStreamDuration(mSelectedMedia.getDuration() * 1000) .build(); RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); remoteMediaClient.load(new MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build());
Consultez également la section sur l'utilisation des pistes multimédias.
Format vidéo 4K
Pour vérifier le format vidéo de votre contenu multimédia, utilisez
getVideoInfo()
dans MediaStatus afin d'obtenir l'instance actuelle de
VideoInfo.
Cette instance contient le type de format TV HDR, ainsi que la hauteur et la largeur d'affichage en pixels. Les variantes du format 4K sont indiquées par les constantes
HDR_TYPE_*.
Notifications de télécommande sur plusieurs appareils
Lorsqu'un utilisateur caste du contenu, les autres appareils Android du même réseau reçoivent une notification leur permettant également de contrôler la lecture. Toute personne dont l'appareil reçoit de telles notifications peut les désactiver pour cet appareil dans l'application Paramètres sous Google > Google Cast > Afficher les notifications de télécommande. (Les notifications incluent un raccourci vers l'application Paramètres.) Pour en savoir plus, consultez Notifications de télécommande Cast.
Ajouter une mini-télécommande
Selon la checklist de conception Cast, une application émettrice doit fournir une commande persistante appelée mini-télécommande qui doit s'afficher lorsque l'utilisateur quitte la page de contenu actuelle pour accéder à une autre partie de l'application émettrice. La mini-télécommande rappelle visuellement à l'utilisateur la session Cast en cours. En appuyant sur la mini-télécommande, l'utilisateur peut revenir à la vue de la télécommande agrandie en plein écran.
Le framework fournit une vue personnalisée, MiniControllerFragment, que vous pouvez ajouter en bas du fichier de mise en page de chaque activité dans laquelle vous souhaitez afficher la mini-télécommande.
<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" />
Lorsque votre application émettrice lit une vidéo ou un flux audio en direct, le SDK affiche automatiquement un bouton de lecture/arrêt à la place du bouton de lecture/pause dans la mini-télécommande.
Pour définir l'apparence du texte du titre et du sous-titre de cette vue personnalisée, et pour choisir les boutons, consultez Personnaliser la mini-télécommande.
Ajouter une télécommande agrandie
La checklist de conception de Google Cast exige qu'une application émettrice fournisse une télécommande agrandie pour le 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, qui s'appelle
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 pour la télécommande agrandie afin d'intégrer l'icône Cast :
<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 une classe qui étend ExpandedControllerActivity.
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 } }
public class ExpandedControlsActivity extends ExpandedControllerActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.expanded_controller, menu); CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item); return true; } }
Dans le manifeste de l'application, déclarez ensuite votre nouvelle activité au sein du tag application :
<application>
...
<activity
android:name=".expandedcontrols.ExpandedControlsActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/Theme.CastVideosDark"
android:screenOrientation="portrait"
android:parentActivityName="com.google.sample.cast.refplayer.VideoBrowserActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
...
</application>
Modifiez CastOptionsProvider, puis changez NotificationOptions et CastMediaOptions de façon à définir l'activité cible sur votre nouvelle activité :
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() }
public CastOptions getCastOptions(Context context) { NotificationOptions notificationOptions = new NotificationOptions.Builder() .setTargetActivityClassName(ExpandedControlsActivity.class.getName()) .build(); CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName()) .build(); return new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build(); }
Mettez à jour la méthode LocalPlayerActivity loadRemoteMedia afin d'afficher votre nouvelle activité lorsque le chargement du contenu multimédia à distance est terminé :
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) { 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(mSelectedMedia) .setAutoplay(autoPlay) .setCurrentTime(position.toLong()).build() ) }
private void loadRemoteMedia(int position, boolean autoPlay) { if (mCastSession == null) { return; } final RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient(); if (remoteMediaClient == null) { return; } remoteMediaClient.registerCallback(new RemoteMediaClient.Callback() { @Override public void onStatusUpdated() { Intent intent = new Intent(LocalPlayerActivity.this, ExpandedControlsActivity.class); startActivity(intent); remoteMediaClient.unregisterCallback(this); } }); remoteMediaClient.load(new MediaLoadRequestData.Builder() .setMediaInfo(mSelectedMedia) .setAutoplay(autoPlay) .setCurrentTime(position).build()); }
Lorsque votre application émettrice lit une vidéo ou un flux audio en direct, le SDK affiche automatiquement un bouton de lecture/arrêt à la place du bouton de lecture/pause dans la télécommande agrandie.
Pour définir l'apparence à l'aide de thèmes, choisir les boutons à afficher, et ajouter des boutons personnalisés, consultez Personnaliser la télécommande agrandie.
Réglage du volume
Le framework gère automatiquement le volume de l'application émettrice. Il synchronise automatiquement les applications émettrice et réceptrice Web afin que l'UI de l'émetteur indique toujours le volume spécifié par le récepteur Web.
Réglage du volume à l'aide des boutons physiques
Sur Android, les boutons physiques de l'appareil émetteur peuvent être utilisés pour modifier le volume de la session Cast sur le récepteur Web par défaut pour tout appareil utilisant Jelly Bean ou une version ultérieure.
Réglage du volume à l'aide des boutons physiques avant Jelly Bean
Pour utiliser les touches de volume physiques afin de contrôler le volume de l'appareil récepteur Web sur
les appareils Android antérieurs à Jelly Bean, l'application émettrice doit remplacer
dispatchKeyEvent
dans ses activités et appeler
CastContext.onDispatchVolumeKeyEventBeforeJellyBean() :
class MyActivity : FragmentActivity() { override fun dispatchKeyEvent(event: KeyEvent): Boolean { return (CastContext.getSharedInstance(this) .onDispatchVolumeKeyEventBeforeJellyBean(event) || super.dispatchKeyEvent(event)) } }
class MyActivity extends FragmentActivity { @Override public boolean dispatchKeyEvent(KeyEvent event) { return CastContext.getSharedInstance(this) .onDispatchVolumeKeyEventBeforeJellyBean(event) || super.dispatchKeyEvent(event); } }
Ajouter des commandes multimédias à la notification et à l'écran de verrouillage
Sur Android uniquement, la checklist de conception de Google Cast exige qu'une application émettrice implémente des commandes multimédias dans une notification et dans l'écran de verrouillage, lorsque l'émetteur caste du contenu, mais que l'application émettrice n'est pas au premier plan. Le
framework fournit
MediaNotificationService
et
MediaIntentReceiver
pour aider l'application émettrice à créer des commandes multimédias dans une notification et dans l'écran de
verrouillage.
MediaNotificationService s'exécute lorsque l'émetteur caste du contenu et affiche une notification contenant une vignette d'image et des informations sur l'élément en cours de diffusion, ainsi qu'un bouton de lecture/pause et un bouton d'arrêt.
MediaIntentReceiver est un BroadcastReceiver qui gère les actions de l'utilisateur à partir de la notification.
Votre application peut configurer les notifications et les commandes multimédias à partir de l'écran de verrouillage via
NotificationOptions.
Votre application peut configurer les boutons de commande à afficher dans la notification et l'Activity à ouvrir lorsque l'utilisateur appuie sur la notification. Si aucune action n'est explicitement fournie, les valeurs par défaut, MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK et MediaIntentReceiver.ACTION_STOP_CASTING, sont utilisées.
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting". val buttonActions: MutableList<String> = ArrayList() buttonActions.add(MediaIntentReceiver.ACTION_REWIND) buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK) buttonActions.add(MediaIntentReceiver.ACTION_FORWARD) buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING) // Showing "play/pause" and "stop casting" in the compat view of the notification. val compatButtonActionsIndices = intArrayOf(1, 3) // Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds. // Tapping on the notification opens an Activity with class VideoBrowserActivity. val notificationOptions = NotificationOptions.Builder() .setActions(buttonActions, compatButtonActionsIndices) .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS) .setTargetActivityClassName(VideoBrowserActivity::class.java.name) .build()
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting". List<String> buttonActions = new ArrayList<>(); buttonActions.add(MediaIntentReceiver.ACTION_REWIND); buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK); buttonActions.add(MediaIntentReceiver.ACTION_FORWARD); buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING); // Showing "play/pause" and "stop casting" in the compat view of the notification. int[] compatButtonActionsIndices = new int[]{1, 3}; // Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds. // Tapping on the notification opens an Activity with class VideoBrowserActivity. NotificationOptions notificationOptions = new NotificationOptions.Builder() .setActions(buttonActions, compatButtonActionsIndices) .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS) .setTargetActivityClassName(VideoBrowserActivity.class.getName()) .build();
L'affichage des commandes multimédias à partir de la notification et de l'écran de verrouillage est activé par
défaut et peut être désactivé en appelant
setNotificationOptions
avec la valeur nulle dans
CastMediaOptions.Builder.
Actuellement, la fonctionnalité de l'écran de verrouillage est activée tant que la fonctionnalité de notification est activée.
// ... continue with the NotificationOptions built above val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .build() val castOptions: CastOptions = Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build()
// ... continue with the NotificationOptions built above CastMediaOptions mediaOptions = new CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) .build(); CastOptions castOptions = new CastOptions.Builder() .setReceiverApplicationId(context.getString(R.string.app_id)) .setCastMediaOptions(mediaOptions) .build();
Lorsque votre application émettrice lit une vidéo ou un flux audio en direct, le SDK affiche automatiquement un bouton de lecture/arrêt à la place du bouton de lecture/pause sur la commande de notification, mais pas sur la commande de l'écran de verrouillage.
Remarque : Pour afficher les commandes de l'écran de verrouillage sur les appareils antérieurs à Lollipop,
RemoteMediaClient demande automatiquement le focus audio en votre nom.
Gérer les erreurs
Il est très important que les applications émettrices gèrent tous les rappels d'erreur et décident de la meilleure réponse pour chaque étape du cycle de vie Cast. L'application peut afficher des boîtes de dialogue d'erreur à l'utilisateur ou décider de rompre la connexion au récepteur Web.