Questa guida per gli sviluppatori descrive come aggiungere il supporto di Google Cast sul tuo dispositivo Android app del mittente utilizzando l'SDK Android Sender.
Il dispositivo mobile o il laptop è il mittente che controlla la riproduzione e Il dispositivo Google Cast è il ricevitore che mostra i contenuti sulla TV.
Il framework del mittente si riferisce al file binario della libreria di classi Cast e al file associato risorse presenti in fase di runtime sul mittente. L'app mittente o l'app Cast si riferisce a un'app in esecuzione anche sul mittente. L'app Web receiver si riferisce all'applicazione HTML in esecuzione sul dispositivo compatibile con Google Cast.
Il framework del mittente utilizza un design di callback asincrono per informare il mittente di eventi e la transizione tra i vari stati dell'attività dell'app Google Cast. ciclo di lancio di Android.
Flusso di app
I passaggi seguenti descrivono il flusso di esecuzione tipico di alto livello per un mittente App per Android:
- Il framework Cast si avvia automaticamente
MediaRouter
rilevamento dei dispositivi in base al ciclo di vita delActivity
. - Quando l'utente fa clic sul pulsante Trasmetti, la struttura presenta la con l'elenco dei dispositivi di trasmissione rilevati.
- Quando l'utente seleziona un dispositivo di trasmissione, il framework tenta di avviare lo strumento App Web Ricevitore sul dispositivo di trasmissione.
- Il framework richiama i callback nell'app del mittente per confermare che il È stata avviata l'app del ricevitore.
- Il framework crea un canale di comunicazione tra il mittente e il web App di ricezione.
- Il framework utilizza il canale di comunicazione per caricare e controllare i contenuti multimediali la riproduzione sul ricevitore web.
- Il framework sincronizza lo stato di riproduzione dei contenuti multimediali tra il mittente e WebRicevitore: quando l'utente esegue azioni nell'interfaccia utente del mittente, il framework supera le richieste di controllo multimediale al ricevitore web e quando quest'ultimo invia aggiornamenti sullo stato dei contenuti multimediali, il framework aggiorna lo stato dell'interfaccia utente del mittente.
- Quando l'utente fa clic sul pulsante Trasmetti per disconnettersi dal dispositivo di trasmissione, il framework disconnetterà l'app del mittente dal ricevitore web.
Per un elenco completo di tutti i corsi, i metodi e gli eventi in Google Cast SDK Android, consulta la documentazione di riferimento sull'API Google Cast Sender per Android. Le sezioni che seguono descrivono i passaggi per aggiungere Cast all'app per Android.
Configurare il file manifest di Android
Il file AndroidManifest.xml della tua app richiede la configurazione di quanto segue per l'SDK Cast:
uses-sdk
Imposta i livelli API Android minimi e target supportati dall'SDK Cast. Attualmente il livello minimo è il livello API 23 e il target è Livello API 34.
<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="34" />
android:theme
Imposta il tema della tua app in base alla versione minima dell'SDK Android. Ad esempio, se
non stai implementando un tema personalizzato, dovresti utilizzare una variante di
Theme.AppCompat
quando scegli come target una versione minima dell'SDK Android che sia
pre-Lollipop.
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat" >
...
</application>
Inizializzare il contesto di trasmissione
Il framework ha un oggetto singleton globale, CastContext
, che coordina
tutte le interazioni del framework.
La tua app deve implementare il
OptionsProvider
a riga di comando per fornire le opzioni necessarie per inizializzare
CastContext
singleton. OptionsProvider
fornisce un'istanza di
CastOptions
che contiene opzioni che influiscono sul comportamento del framework. Il più
di questi è importante l'ID applicazione Web receiver, usato per filtrare
dei risultati del rilevamento e avviare l'app Web Ricevitore quando viene avviata una sessione di trasmissione.
a iniziare.
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; } }
Devi dichiarare il nome completo dell'oggetto OptionsProvider
implementato
come campo di metadati nel file AndroidManifest.xml dell'app del mittente:
<application>
...
<meta-data
android:name=
"com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.foo.CastOptionsProvider" />
</application>
CastContext
viene inizializzato in modo lento quando CastContext.getSharedInstance()
.
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); } }
Widget UX di Cast
Il framework Cast fornisce widget conformi al design Cast Elenco di controllo:
Overlay introduttivo: Il framework fornisce visualizzazioni,
IntroductoryOverlay
, che viene mostrato all'utente per richiamare l'attenzione sul pulsante Trasmetti. quando un destinatario è disponibile per la prima volta. L'app Mittente può personalizza il testo e la posizione del titolo testo.Pulsante Trasmetti: Il pulsante Trasmetti è visibile indipendentemente dalla disponibilità dei dispositivi di trasmissione. Quando l'utente fa clic per la prima volta sul pulsante Trasmetti, viene visualizzata una finestra di dialogo Trasmetti. che elenca i dispositivi rilevati. Quando l'utente fa clic sul pulsante Trasmetti. Mentre il dispositivo è connesso, vengono visualizzati i metadati multimediali correnti (come titolo, il nome dello studio di registrazione e un'immagine in miniatura) o consente all'utente per disconnetterti dal dispositivo di trasmissione. Il "pulsante Trasmetti" a volte viene definito come "icona Trasmetti".
Mini controller: Quando l'utente sta trasmettendo contenuti ed è uscito dalla versione corrente pagina dei contenuti o controller espanso su un altro schermo nell'app del mittente, il mini controller viene mostrato nella parte inferiore dello schermo per consentire all'utente di vedere i metadati multimediali attualmente trasmessi e controllare la riproduzione.
Controller espanso: Quando l'utente trasmette contenuti, se fa clic sulla notifica multimediale o mini controller, si avvia il controller espanso, che mostra sono attualmente in riproduzione metadati dei contenuti multimediali e fornisce diversi pulsanti per controllare la riproduzione dei contenuti multimediali.
Notifica: Solo su Android. Quando l'utente trasmette contenuti ed esce dalla schermata dell'app del mittente, viene visualizzata una notifica di contenuti multimediali che mostra la trasmissione in corso metadati multimediali e controlli di riproduzione.
Schermata di blocco: Solo su Android. Quando l'utente trasmette contenuti e naviga (o il dispositivo) timeout) alla schermata di blocco, viene visualizzato un controllo della schermata di blocco dei contenuti multimediali che mostra i metadati dei contenuti multimediali e i controlli di riproduzione attualmente trasmessi.
La seguente guida include le descrizioni su come aggiungere questi widget a la tua app.
Aggiungi un pulsante Trasmetti
Android
MediaRouter
Le API sono progettate per abilitare la visualizzazione e la riproduzione di contenuti multimediali su dispositivi secondari.
Le app per Android che utilizzano l'API MediaRouter
devono includere un pulsante Trasmetti
dell'interfaccia utente, per consentire agli utenti di selezionare un percorso multimediale su cui riprodurre
su un dispositivo secondario,
ad esempio un dispositivo di trasmissione.
Il framework semplifica l'aggiunta
MediaRouteButton
come
Cast button
molto facile. Devi prima aggiungere una voce di menu o MediaRouteButton
nel file XML
che definisce il menu e utilizza
CastButtonFactory
per collegarlo al 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; }
Quindi, se il tuo Activity
eredita da
FragmentActivity
,
puoi aggiungere
MediaRouteButton
al tuo layout.
// 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); }
Per impostare l'aspetto del pulsante Trasmetti utilizzando un tema, visita la pagina Personalizza pulsante Trasmetti.
Configura il rilevamento dei dispositivi
Il rilevamento dei dispositivi è completamente gestito
CastContext
Durante l'inizializzazione di CastContext, l'app del mittente specifica il ricevitore web
e può richiedere il filtro dello spazio dei nomi impostando
supportedNamespaces
pollici
CastOptions
CastContext
contiene internamente un riferimento a MediaRouter
e inizierà
il processo di rilevamento alle seguenti condizioni:
- Si basano su un algoritmo progettato per bilanciare la latenza del rilevamento dei dispositivi e batteria, il rilevamento occasionalmente verrà avviato automaticamente l'app del mittente entra in primo piano.
- La finestra di dialogo Trasmetti è aperta.
- L'SDK Cast sta tentando di recuperare una sessione di Cast.
Il processo di rilevamento viene interrotto quando la finestra di dialogo Trasmetti viene chiusa o l'app del mittente entra in background.
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; } }
Come funziona la gestione delle sessioni
L'SDK Cast introduce il concetto di sessione di trasmissione, il la cui creazione combina i passaggi per connettersi a un dispositivo, avviare (o accedere) a un App ricevitore, connessione all'app e inizializzazione di un canale di controllo multimediale. Guarda il ricevitore web Guida al ciclo di vita dell'applicazione per ulteriori informazioni sulle sessioni di trasmissione e sul ciclo di vita del ricevitore web.
Le sessioni sono gestite dalla classe
SessionManager
,
a cui può accedere la tua app tramite
CastContext.getSessionManager()
.
Le singole sessioni sono rappresentate dalle sottoclassi della classe
Session
Ad esempio:
CastSession
rappresenta le sessioni con dispositivi di trasmissione. La tua app può accedere ai contenuti attualmente attivi
Sessione di trasmissione tramite
SessionManager.getCurrentCastSession()
La tua app può utilizzare
SessionManagerListener
per monitorare gli eventi delle sessioni, come creazione, sospensione, ripresa
la risoluzione. Il framework tenta automaticamente di riprendere
interruzione anomala/brusca mentre una sessione era attiva.
Le sessioni vengono create e ridotte automaticamente in risposta ai gesti dell'utente.
dalle finestre di dialogo MediaRouter
.
Per comprendere meglio gli errori di avvio della trasmissione, le app possono usare
CastContext#getCastReasonCodeForCastStatusCode(int)
per convertire l'errore di avvio della sessione in
CastReasonCodes
.
Tieni presente che alcuni errori di avvio della sessione (ad es. CastReasonCodes#CAST_CANCELLED
)
sono comportamenti previsti e non devono essere registrati come errori.
Se devi essere a conoscenza dei cambiamenti di stato della sessione, puoi implementare
SessionManagerListener
. Questo esempio prende in considerazione la disponibilità di un
CastSession
in 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); } }
Trasferimento dello streaming
La conservazione dello stato della sessione è alla base del trasferimento dello streaming, dove gli utenti possono spostare gli stream audio e video esistenti su più dispositivi con i comandi vocali e Google Home all'app o a smart display. La riproduzione dei contenuti multimediali si interrompe su un dispositivo (l'origine) e continua su un altro (lo destinazione). Qualsiasi dispositivo di trasmissione con il firmware più recente può fungere da sorgente o destinazione in un trasferimento dello streaming.
Per ricevere il nuovo dispositivo di destinazione durante un trasferimento o un'espansione dello streaming:
registra un
Cast.Listener
utilizzando
CastSession#addCastListener
.
Quindi chiama
CastSession#getCastDevice()
durante il callback onDeviceNameChanged
.
Consulta: Trasferimento dello streaming sul ricevitore web per ulteriori informazioni.
Riconnessione automatica
Il framework fornisce
ReconnectionService
che può essere attivata dall'app del mittente
per gestire la riconnessione in molte
casi d'angolo, come:
- Recuperare dopo una perdita temporanea del Wi-Fi
- Ripristina dalla sospensione del dispositivo
- Recuperare l'app in background
- Ripristinare se l'app ha avuto un arresto anomalo
Questo servizio è attivo per impostazione predefinita e può essere disattivato in
CastOptions.Builder
Questo servizio può essere unito automaticamente al file manifest dell'app se viene creata l'unione automatica sia abilitato nel tuo file Gradle.
Il framework avvierà il servizio e lo arresta quando è in corso una sessione multimediale al termine della sessione multimediale.
Come funziona Controllo contenuti multimediali
Il framework Cast depreca la
RemoteMediaPlayer
di Cast 2.x a favore di un nuovo corso
RemoteMediaClient
,
che offre la stessa funzionalità in un insieme di API più convenienti e
evita di dover passare in un GoogleApiClient.
Quando l'app stabilisce
CastSession
con un'app WebRicevitore che supporta lo spazio dei nomi multimediale, un'istanza
RemoteMediaClient
verrà creato automaticamente dal framework; la tua app può
per accedervi chiamando il metodo getRemoteMediaClient()
su CastSession
in esecuzione in un'istanza Compute Engine.
Tutti i metodi di RemoteMediaClient
che inviano richieste al ricevitore web
restituisce un oggetto PendingResult che può essere utilizzato per tenere traccia della richiesta.
Si prevede che l'istanza di RemoteMediaClient
possa essere condivisa
più parti dell'app e alcuni componenti interni
come i mini controller permanenti e
il servizio di notifica.
A questo scopo, l'istanza supporta la registrazione di più istanze
RemoteMediaClient.Listener
.
Imposta metadati multimediali
La
MediaMetadata
rappresenta le informazioni su un elemento multimediale che vuoi trasmettere. La
nell'esempio seguente crea una nuova istanza MediaMetadata di un film e imposta
titolo, sottotitolo e due immagini.
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))));
Consulta: Selezione delle immagini sull'uso di immagini con metadati multimediali.
Carica contenuti multimediali
La tua app può caricare un elemento multimediale, come mostrato nel codice seguente. Primo utilizzo
MediaInfo.Builder
con i metadati dei contenuti multimediali per creare
MediaInfo
in esecuzione in un'istanza Compute Engine. Scarica
RemoteMediaClient
dal CastSession
corrente, quindi carica MediaInfo
RemoteMediaClient
. Usa RemoteMediaClient
per riprodurre, mettere in pausa e altro ancora
controllare l'app di un lettore multimediale in esecuzione sul ricevitore 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());
Vedi anche la sezione su utilizzando tracce multimediali.
Formato video 4K
Per verificare di quale formato video sono i tuoi contenuti multimediali, usa
getVideoInfo()
in MediaStatus per ottenere l'istanza attuale
VideoInfo
.
Questa istanza contiene il tipo di formato TV HDR e l'altezza di visualizzazione
e la larghezza in pixel. Le varianti del formato 4K sono indicate da costanti
HDR_TYPE_*
Notifiche da remoto per più dispositivi
Quando un utente trasmette, gli altri dispositivi Android sulla stessa rete riceveranno una notifica per consentire anche di controllare la riproduzione. Chiunque abbia il dispositivo riceve notifiche di questo tipo può disattivarle per quel dispositivo nelle Impostazioni app su Google > Google Cast > Mostra notifiche di controllo remoto. Le notifiche includono una scorciatoia per l'app Impostazioni. Per ulteriori dettagli, vedi Notifiche di controllo remoto della trasmissione.
Aggiungi mini controller
In base al design del cast elenco di controllo, L'app mittente deve fornire un controllo permanente noto come mini un controller che dovrebbe essere visualizzata quando l'utente esce dalla pagina dei contenuti corrente per un'altra parte dell'app del mittente. Il mini controller fornisce un promemoria visibile all'utente della sessione di trasmissione corrente. Se tocchi il mini controller, l'utente può tornare alla visualizzazione espansa del controller Trasmetti a schermo intero.
Il framework fornisce una vista personalizzata, MiniControllerFragment, che puoi aggiungere in fondo al file di layout di ogni attività in cui vuoi mostrare un mini controller.
<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" />
Quando l'app del mittente riproduce un live streaming video o audio, l'SDK mostra automaticamente un pulsante di riproduzione/interruzione al posto del pulsante di riproduzione/pausa nel mini controller.
Per impostare l'aspetto del testo del titolo e del sottotitolo di questa visualizzazione personalizzata: e per scegliere i pulsanti, vedi Personalizza il mini controller.
Aggiungi controller espanso
L'elenco di controllo per la progettazione di Google Cast richiede che l'app del mittente fornisca un un controller per i contenuti multimediali trasmessi. Il controller espanso è una versione a schermo intero il mini controller.
L'SDK Cast fornisce un widget per il controller espanso chiamato
ExpandedControllerActivity
Si tratta di una classe astratta di cui devi eseguire una sottoclasse per aggiungere un pulsante Trasmetti.
Innanzitutto, crea un nuovo file di risorse del menu che il controller espanso possa fornire il pulsante Trasmetti:
<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>
Crea un nuovo corso che estende 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; } }
Ora dichiara la nuova attività nel file manifest dell'app all'interno del 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>
Modifica CastOptionsProvider
e cambia NotificationOptions
e
CastMediaOptions
per impostare l'attività target sulla nuova attività:
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(); }
Aggiorna il metodo loadRemoteMedia
LocalPlayerActivity
per visualizzare
nuova attività quando viene caricato il contenuto multimediale remoto:
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()); }
Quando l'app del mittente riproduce un live streaming video o audio, l'SDK mostra automaticamente un pulsante di riproduzione/interruzione al posto del pulsante di riproduzione/pausa nel controller espanso.
Per impostare l'aspetto con i temi, scegli i pulsanti da visualizzare, e aggiungere pulsanti personalizzati, vedi Personalizza il controller espanso.
Controllo del volume
Il framework gestisce automaticamente il volume per l'app del mittente. Il framework sincronizza automaticamente le app del mittente e del ricevitore web in modo che il mittente La UI riporta sempre il volume specificato dal ricevitore web.
Controllo del volume tramite tasto fisico
Su Android, i pulsanti fisici sul dispositivo del mittente possono essere utilizzati per modificare volume della sessione di trasmissione sul ricevitore web per impostazione predefinita per qualsiasi dispositivo che utilizza Jelly Bean o più recente.
Controllo del volume del tasto fisico prima di Jelly Bean
Per utilizzare i tasti fisici del volume per regolare il volume del dispositivo Web Ricevitore su
Dispositivi Android precedenti a Jelly Bean, l'app del mittente dovrebbe eseguire l'override
dispatchKeyEvent
nelle loro attività e richiamano
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); } }
Aggiungi controlli multimediali a notifiche e schermata di blocco
Solo su Android, l'elenco di controllo per la progettazione di Google Cast richiede un'app mittente per
di implementare i controlli multimediali
notifica
e nella
schermo,
dove il mittente sta trasmettendo, ma l'app del mittente non è in evidenza. La
il framework fornisce
MediaNotificationService
e
MediaIntentReceiver
per aiutare l'app del mittente a creare controlli multimediali nelle notifiche e nella serratura
schermo.
MediaNotificationService
viene eseguito quando il mittente trasmette e mostrerà
una notifica con miniatura dell'immagine e informazioni sulla trasmissione in corso
elemento, un pulsante di riproduzione/pausa e un pulsante di interruzione.
MediaIntentReceiver
è un BroadcastReceiver
che gestisce le azioni degli utenti da
la notifica.
L'app può configurare le notifiche e il controllo multimediale dalla schermata di blocco tramite
NotificationOptions
L'app può configurare quali pulsanti di controllo mostrare nella notifica.
che Activity
aprire quando l'utente tocca la notifica. Se azioni
vengono forniti esplicitamente, i valori predefiniti
MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK
e
Verrà utilizzato MediaIntentReceiver.ACTION_STOP_CASTING
.
// 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();
I controlli multimediali nelle notifiche e nella schermata di blocco sono attivati da
predefinita e può essere disattivata chiamando
setNotificationOptions
con null in
CastMediaOptions.Builder
.
Al momento, la funzionalità di schermata di blocco è attiva purché la notifica sia
attivata.
// ... 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();
Quando l'app del mittente riproduce un live streaming video o audio, l'SDK mostra automaticamente un pulsante di riproduzione/interruzione al posto del pulsante di riproduzione/pausa nel controllo delle notifiche, ma non in quello della schermata di blocco.
Nota: per visualizzare i controlli della schermata di blocco sui dispositivi con versioni precedenti a Lollipop,
RemoteMediaClient
richiederà automaticamente il focus audio per tuo conto.
Gestire gli errori
È molto importante che le app dei mittenti gestiscano tutti i callback di errore e stabiliscano la risposta migliore per ogni fase del ciclo di vita della trasmissione. L'app può visualizzare finestre di dialogo di errore per l'utente oppure può decidere di interrompere la connessione Ricevitore web.