Ce guide du développeur explique comment ajouter la prise en charge de Google Cast à l'application émettrice Android à l'aide du SDK Android Sender.
L'appareil mobile ou l'ordinateur portable est l'expéditeur qui contrôle la lecture, tandis que l'appareil Google Cast est le destinataire qui affiche le contenu sur le téléviseur.
Le framework d'envoi 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'expéditeur. L'application émettrice ou l'application Cast fait référence à une application qui s'exécute également sur l'expéditeur. L'application Web Receiver fait référence à l'application HTML exécutée sur l'appareil compatible Cast.
Le framework d'expéditeur utilise une conception de rappel asynchrone pour informer l'application émettrice des événements et pour qu'elle passe des différents états 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 de haut niveau typique d'une application Android émettrice:
- Le framework Cast lance automatiquement la détection d'appareils
MediaRouter
en fonction du cycle de vie deActivity
. - Lorsque l'utilisateur clique sur l'icône Cast, le framework présente 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 Web Receiver sur l'appareil Cast.
- Le framework appelle des rappels dans l'application expéditeur pour confirmer que l'application Web Receiver a été lancée.
- Le framework crée un canal de communication entre l'application expéditeur et les applications du destinataire Web.
- Le framework utilise le canal de communication pour charger et contrôler la lecture des contenus multimédias sur le récepteur Web.
- Le framework synchronise l'état de lecture du contenu multimédia entre l'expéditeur et le destinataire Web. Lorsque l'utilisateur effectue des actions dans l'interface utilisateur de l'expéditeur, le framework transmet ces requêtes de commandes multimédias 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'interface utilisateur de l'expéditeur.
- Lorsque l'utilisateur clique sur l'icône Cast pour se déconnecter de l'appareil Cast, le framework déconnecte l'application expéditeur du récepteur Web.
Pour obtenir la liste complète des classes, des méthodes et des événements du SDK Google Cast pour Android, consultez la documentation de référence de l'API Google Cast Sender pour Android. Les sections suivantes vous expliquent comment ajouter Cast à votre application Android.
Configurer le fichier manifeste Android
Le fichier AndroidManifest.xml de votre application nécessite la configuration des éléments suivants pour le SDK Cast:
uses-sdk
Définissez les niveaux d'API Android minimaux et cibles compatibles avec le SDK Cast. Le niveau minimal est actuellement de 19 et le niveau d'API cible 28.
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="28" />
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 comporte un objet singleton global, CastContext
, qui coordonne toutes les interactions du framework.
Votre application doit implémenter l'interface OptionsProvider
pour fournir les options nécessaires à l'initialisation du Singleton CastContext
. OptionsProvider
fournit une instance de CastOptions
qui contient des options qui affectent le comportement du framework. Le plus important est l'ID d'application du récepteur Web, qui permet de filtrer les résultats de la découverte et de lancer l'application du récepteur 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 du OptionsProvider
implémenté en tant que champ de métadonnées dans le fichier AndroidManifest.xml de l'application expéditeur:
<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'expérience utilisateur Cast
Le framework Cast fournit les widgets conformes à la checklist de conception Cast:
Superposition d'introduction : le framework fournit une vue personnalisée,
IntroductoryOverlay
, présentée à l'utilisateur pour attirer l'attention sur l'icône Cast la première fois qu'un récepteur est disponible. L'application Expéditeur peut personnaliser le texte et la position du texte du titre.Cast Cast : l'icône Cast est visible lorsqu'un destinataire est compatible avec votre application. Lorsque l'utilisateur clique pour la première fois sur l'icône Cast, une boîte de dialogue Cast s'affiche. Elle liste les appareils détectés. Lorsque l'utilisateur clique sur l'icône Cast lorsque l'appareil est connecté, les métadonnées multimédias actuelles (telles que le titre, le nom du studio d'enregistrement et une vignette) s'affichent, ou l'utilisateur peut se déconnecter de l'appareil Cast.
Mini-manette : lorsque l'utilisateur caste du contenu et a quitté la page de contenu actuelle ou la manette étendue vers un autre écran de l'application expéditeur, la mini-télécommande s'affiche en bas de l'écran pour permettre à l'utilisateur de voir les métadonnées multimédias en cours de diffusion et de contrôler la lecture.
Manette étendue : lorsque l'utilisateur caste du contenu, s'il clique sur la notification multimédia ou la mini-télécommande, elle se lance. Elle affiche les métadonnées multimédias en cours de lecture et fournit plusieurs boutons pour contrôler la lecture.
Notification : Android uniquement. Lorsque l'utilisateur caste du contenu et quitte l'application émettrice, une notification multimédia s'affiche. Elle contient 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 arrive à expiration), une commande de verrouillage multimédia s'affiche. Elle indique les métadonnées multimédias et les commandes de lecture en cours de diffusion.
Le guide suivant explique comment ajouter ces widgets à votre application.
Ajouter un 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 un icône Cast dans leur interface utilisateur afin de permettre aux utilisateurs de sélectionner une route multimédia pour lire les contenus multimédias sur un appareil secondaire tel qu'un appareil Cast.
Le framework permet d'ajouter très facilement une MediaRouteButton
en tant que Cast button
. Vous devez d'abord ajouter un élément de menu ou un élément 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; }
Si votre Activity
hérite ensuite 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 sur un thème, consultez Personnaliser l'icône Cast.
Configurer la détection d'appareils
La détection d'appareils est entièrement gérée par CastContext
.
Lors de l'initialisation de CastContext, l'application expéditeur 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écouverte lorsque l'application expéditeur passe au premier plan et s'arrête lorsque l'application expéditeur 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, qui consiste à associer les étapes suivantes : connexion à un appareil, lancement (ou inscription) d'une application Web receiver, connexion à cette application et initialisation d'un canal de contrôle multimédia. Pour en savoir plus sur les sessions Cast et le cycle de vie du récepteur Web, consultez le Guide du cycle de vie de l'application.
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 classe SessionManagerListener
pour surveiller les événements de session, tels que la création, la suspension, la reprise et l'arrêt. Le framework tente automatiquement de reprendre après une interruption anormale ou soudaine pendant qu'une session était active.
Les sessions sont créées et supprimées automatiquement en réponse aux gestes des utilisateurs à partir des boîtes de dialogue MediaRouter
.
Pour mieux comprendre les erreurs de démarrage Cast, les applications peuvent utiliser CastContext#getCastReasonCodeForCastStatusCode(int)
afin de convertir l'erreur de démarrage de session en CastReasonCodes
.
Veuillez noter que certaines erreurs de démarrage de session (par exemple, CastReasonCodes#CAST_CANCELLED
) sont intentionnelles et ne doivent pas être consignées en tant qu'erreurs.
Si vous devez connaître les changements d'état pour la session, vous pouvez implémenter un SessionManagerListener
. Cet exemple écoute la disponibilité d'un CastSession
dans un 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 } override fun onResume() { super.onResume() mCastSession = mSessionManager.currentCastSession mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java) } override fun onPause() { super.onPause() mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java) mCastSession = null } }
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(); } @Override protected void onResume() { super.onResume(); mCastSession = mSessionManager.getCurrentCastSession(); mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class); } @Override protected void onPause() { super.onPause(); mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class); mCastSession = null; } }
Transfert de diffusion
Conserver l'état de la session est la base du transfert de flux, où les utilisateurs peuvent déplacer des flux audio et vidéo existants sur des appareils à l'aide de commandes vocales, de l'application Google Home ou d'écrans connectés. La lecture du contenu multimédia s'arrête sur un appareil (la source) et se poursuit sur un autre (la destination). Tout appareil Cast doté du dernier micrologiciel peut servir de sources ou de destinations dans un transfert de diffusion.
Pour obtenir le nouvel appareil de destination lors d'un transfert ou d'un expansion de flux, enregistrez un Cast.Listener
à l'aide de CastSession#addCastListener
.
Appelez ensuite CastSession#getCastDevice()
lors du rappel onDeviceNameChanged
.
Pour en savoir plus, consultez la page 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 subtils, par exemple:
- Récupérer après une perte temporaire du Wi-Fi
- Récupérer de l'appareil en veille
- Récupérer après la mise en arrière-plan de l'application
- Récupérer si l'appli a planté
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émarrera le service à la fin d'une session multimédia et l'arrêtera à la fin de cette session.
Fonctionnement des commandes multimédias
Le framework Cast abandonne la classe RemoteMediaPlayer
au profit de Cast 2.x au profit d'une nouvelle classe RemoteMediaClient
, qui fournit les mêmes fonctionnalités dans un ensemble d'API plus pratiques et évite de transmettre un GoogleApiClient.
Lorsque votre application établit une CastSession
avec une application Web Receiver 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 envoient des requêtes au récepteur Web renvoient un objet PendingResult qui peut être utilisé pour suivre cette requête.
Il est normal que l'instance de RemoteMediaClient
puisse être partagée par plusieurs parties de votre application, et donc par certains composants internes du framework, tels que les mini-contrôleurs persistants et le service de notification.
À cette fin, cette instance permet d'enregistrer plusieurs instances de RemoteMediaClient.Listener
.
Définir les métadonnées du contenu multimédia
La classe MediaMetadata
représente les informations sur un élément multimédia que vous souhaitez caster. 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 la section Sélection d'images sur l'utilisation d'images avec des métadonnées multimédias.
Charger le 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 support pour créer une instance MediaInfo
. Obtenez le RemoteMediaClient
à partir du CastSession
actuel, puis chargez le MediaInfo
dans ce RemoteMediaClient
. Utilisez RemoteMediaClient
pour lire, mettre en pause et contrôler d'une autre manière une application de lecteur multimédia s'exécutant 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 connaître 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'écran en pixels. Les variantes 4K sont indiquées par des constantes HDR_TYPE_*
.
Notifications sur plusieurs appareils à distance
Lorsqu'un utilisateur caste, d'autres appareils Android du même réseau reçoivent une notification leur permettant de contrôler la lecture. Toute personne dont l'appareil reçoit ces notifications peut les désactiver pour cet appareil dans l'application Paramètres, dans Google > Google Cast > Afficher les notifications de la télécommande. (Les notifications incluent un raccourci vers l'application Paramètres.) Pour en savoir plus, consultez Notifications de la télécommande Cast.
Ajouter une mini-télécommande
D'après la checklist de conception de Cast, une application émettrice doit fournir une commande persistante appelée mini-contrôleur qui doit s'afficher lorsque l'utilisateur quitte la page de contenu actuelle et se tourne vers une autre partie de l'application émettrice. La mini-télécommande fournit un rappel visible à l'utilisateur de la session Cast en cours. En appuyant sur la mini-télécommande, l'utilisateur peut revenir à la vue étendue de la manette Cast 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 émetteur lit un flux vidéo en direct ou audio, 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 des boutons, consultez Personnaliser la mini-télécommande.
Ajouter une manette agrandie
La checklist de conception Google Cast exige qu'une application émettrice fournisse un contrôleur étendu pour le contenu multimédia en cours de diffusion. La manette agrandie est une version plein écran de la mini-télécommande.
Le SDK Cast fournit un widget pour le contrôleur étendu 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 pour que la manette étendue puisse fournir 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; } }
Déclarez maintenant votre nouvelle activité dans le fichier manifeste de l'application dans la balise 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
et NotificationOptions
et CastMediaOptions
pour 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
pour afficher votre nouvelle activité une fois le contenu multimédia distant chargé:
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 l'application émetteur lit un flux vidéo ou 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, choisissez les boutons à afficher et ajoutez des boutons personnalisés, consultez la section Personnaliser la manette étendue.
Contrôle du volume
Le framework gère automatiquement le volume pour l'application de l'expéditeur. Il synchronise automatiquement les applications de l'expéditeur et du récepteur Web afin que l'interface utilisateur de l'expéditeur indique toujours le volume spécifié par le récepteur Web.
Commande du volume du bouton physique
Sur Android, les boutons physiques de l'appareil émetteur peuvent être utilisés par défaut pour modifier le volume de la session Cast sur le récepteur Web pour tout appareil utilisant Jelly Bean ou une version ultérieure.
Commande du volume du bouton physique avant Jelly Bean
Pour utiliser les touches de volume physiques pour contrôler le volume de l'appareil du récepteur Web sur les appareils Android antérieurs à Jelly Bean, l'application de l'expéditeur 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 aux notifications et à l'écran de verrouillage
Sur Android uniquement, la checklist de conception Google Cast nécessite que l'application de l'expéditeur implémente les commandes multimédias dans une notification, ainsi que sur l'écran de verrouillage où l'expéditeur caste du contenu, mais n'est pas sélectionné. Le framework fournit MediaNotificationService
et MediaIntentReceiver
pour aider l'application émettrice à créer des commandes multimédias dans une notification et sur l'écran de verrouillage.
MediaNotificationService
s'exécute lorsque l'émetteur caste du contenu. Une notification s'affiche, avec une vignette de l'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 des utilisateurs à partir de la notification.
Votre application peut configurer les notifications et le contrôle multimédia depuis l'écran de verrouillage via NotificationOptions
.
Votre application peut configurer les boutons de commande à afficher dans la notification et le Activity
à ouvrir lorsque l'utilisateur appuie sur la notification. Si les actions ne sont pas explicitement fournies, 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 depuis les notifications et l'écran de verrouillage est activé par défaut. Vous pouvez le désactiver en appelant setNotificationOptions
avec la valeur "null" dans CastMediaOptions.Builder
.
Actuellement, la fonctionnalité de verrouillage d'écran est activée tant que la 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 l'application émetteur lit un flux vidéo en direct ou audio, 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 d'écran de verrouillage.
Remarque: Pour afficher les commandes de l'écran de verrouillage sur les appareils antérieurs à Lollipop, RemoteMediaClient
demandera automatiquement la sélection 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 à chaque étape du cycle de vie de Cast. L'application peut afficher des boîtes de dialogue d'erreur ou décider de supprimer la connexion au récepteur Web.