1. Panoramica
Questo codelab ti insegnerà a modificare un'app video per Android esistente per trasmettere contenuti su un dispositivo compatibile con Google Cast.
Che cos'è Google Cast?
Google Cast consente agli utenti di trasmettere contenuti da un dispositivo mobile a una TV. Gli utenti possono quindi utilizzare il proprio dispositivo mobile come telecomando per la riproduzione di contenuti multimediali sulla TV.
L'SDK Google Cast ti consente di estendere la tua app per controllare la TV o l'impianto audio. L'SDK Cast ti consente di aggiungere i componenti UI necessari in base all'elenco di controllo per la progettazione di Google Cast.
Viene fornito un elenco di controllo per la progettazione di Google Cast per rendere l'esperienza utente di Cast semplice e prevedibile su tutte le piattaforme supportate.
Cosa realizzeremo
Una volta completato questo codelab, avrai a disposizione un'app video per Android in grado di trasmettere video a un dispositivo compatibile con Google Cast.
Obiettivi didattici
- Come aggiungere l'SDK Google Cast a un'app video di esempio.
- Come aggiungere il pulsante Trasmetti per selezionare un dispositivo Google Cast.
- Come connetterti a un dispositivo di trasmissione e avviare un ricevitore multimediale.
- Come trasmettere un video.
- Come aggiungere un controller Cast Mini alla tua app.
- Come supportare le notifiche multimediali e i controlli della schermata di blocco.
- Come aggiungere un controller espanso.
- Come fornire un overlay introduttivo.
- Come personalizzare i widget di trasmissione.
- Come eseguire l'integrazione con Cast Connect
Che cosa ti serve
- L'SDK Android più recente.
- Android Studio 3.2 o versioni successive
- Un dispositivo mobile con Android 4.1 Jelly Bean e versioni successive (livello API 16).
- Un cavo dati USB per collegare il dispositivo mobile al computer di sviluppo.
- Un dispositivo Google Cast, ad esempio Chromecast o Android TV, configurato con accesso a internet.
- Una TV o un monitor con ingresso HDMI.
- Per testare l'integrazione di Cast Connect è necessario un Chromecast con Google TV, ma è facoltativo per la parte restante del codelab. Se non ne hai uno, salta il passaggio Aggiungere assistenza per Cast Connect verso la fine di questo tutorial.
Esperienza
- È richiesta una conoscenza pregressa dello sviluppo di Kotlin e Android.
- È necessaria anche una conoscenza pregressa di come guardare la TV :)
Come userai questo tutorial?
Come valuteresti la tua esperienza con lo sviluppo di app Android?
Come valuteresti la tua esperienza con la visione della TV?
2. Recupera il codice campione
Puoi scaricare tutto il codice campione sul tuo computer...
e decomprimere il file ZIP scaricato.
3. Esegui l'app di esempio
Innanzitutto, vediamo l'aspetto dell'app di esempio completata. L'app è un video player di base. L'utente può selezionare un video da un elenco e riprodurlo localmente sul dispositivo oppure trasmetterlo a un dispositivo Google Cast.
Dopo aver scaricato il codice, le istruzioni seguenti spiegano come aprire ed eseguire l'app di esempio completata in Android Studio:
Seleziona Importa progetto nella schermata di benvenuto o le opzioni di menu File > Nuovo > Importa progetto....
Seleziona la directory app-done
dalla cartella del codice di esempio e fai clic su OK.
Fai clic su File > Sincronizza progetto con file Gradle.
Attiva il debug USB sul tuo dispositivo Android. Su Android 4.2 e versioni successive, la schermata Opzioni sviluppatore è nascosta per impostazione predefinita. Per renderlo visibile, vai a Impostazioni > Informazioni sul telefono e tocca Numero build sette volte. Torna alla schermata precedente, vai a Sistema > Avanzate e tocca Opzioni sviluppatore in basso, quindi tocca Debug USB per attivarlo.
Collega il tuo dispositivo Android e fai clic sul pulsante Esegui in Android Studio. L'app video denominata Trasmetti video dovrebbe essere visualizzata dopo qualche secondo.
Fai clic sul pulsante Trasmetti nell'app video e seleziona il tuo dispositivo Google Cast.
Seleziona un video e fai clic sul pulsante di riproduzione.
La riproduzione del video inizierà sul tuo dispositivo Google Cast.
Verrà visualizzato il controller espanso. Puoi utilizzare il pulsante di riproduzione/pausa per controllare la riproduzione.
Torna all'elenco dei video.
Ora è visibile un mini controller nella parte inferiore dello schermo.
Fai clic sul pulsante Pausa nel mini controller per mettere in pausa il video sul ricevitore. Fai clic sul pulsante di riproduzione nel mini controller per continuare a riprodurre il video.
Fai clic sul pulsante Home del dispositivo mobile. Trascina verso il basso le notifiche; ora dovresti trovare una notifica relativa alla sessione di trasmissione.
Blocca il telefono. Quando lo sblocchi, dovresti visualizzare una notifica nella schermata di blocco per controllare la riproduzione dei contenuti multimediali o interrompere la trasmissione.
Torna all'app video e fai clic sul pulsante Trasmetti per interrompere la trasmissione sul dispositivo Google Cast.
Domande frequenti
4. Prepara il progetto di avvio
Dobbiamo aggiungere il supporto di Google Cast all'app iniziale che hai scaricato. Ecco alcuni termini di Google Cast che utilizzeremo in questo codelab:
- Un'app del mittente viene eseguita su un dispositivo mobile o un laptop,
- Sul dispositivo Google Cast è in esecuzione un'app per ricevitore.
Ora è tutto pronto per creare sulla base del progetto iniziale utilizzando Android Studio:
- Seleziona la directory
app-start
dal download del codice campione (seleziona Importa progetto nella schermata di benvenuto o l'opzione di menu File > Nuovo > Importa progetto...). - Fai clic sul pulsante Sincronizza progetto con file Gradle.
- Fai clic sul pulsante Esegui per eseguire l'app ed esplorare l'interfaccia utente.
Progettazione di app
L'app recupera un elenco di video da un server web remoto e fornisce all'utente un elenco per la navigazione. Gli utenti possono selezionare un video per visualizzarne i dettagli o riprodurlo localmente sul dispositivo mobile.
L'app è costituita da due attività principali: VideoBrowserActivity
e LocalPlayerActivity
. Per integrare la funzionalità Google Cast, le attività devono ereditare dalla AppCompatActivity
o dal relativo elemento principale FragmentActivity
. Questa limitazione esiste perché dobbiamo aggiungere MediaRouteButton
(fornito nella libreria di supporto MediaRouter) come MediaRouteActionProvider
e funziona solo se l'attività eredita dalle classi sopra citate. La libreria di supporto di MediaRouter dipende dalla libreria di supporto di AppCompat che fornisce le classi richieste.
VideoBrowserActivity
Questa attività contiene un elemento Fragment
(VideoBrowserFragment
). Questo elenco è supportato da un ArrayAdapter
(VideoListAdapter
). L'elenco dei video e i relativi metadati sono ospitati su un server remoto come file JSON. Un AsyncTaskLoader
(VideoItemLoader
) recupera questo JSON e lo elabora per creare un elenco di MediaItem
oggetti.
Un oggetto MediaItem
modella un video e i metadati associati, come il titolo, la descrizione, l'URL dello stream, l'URL delle immagini di supporto e le eventuali tracce di testo associate (per i sottotitoli codificati). L'oggetto MediaItem
viene passato tra le attività, quindi MediaItem
dispone di metodi di utilità per convertirlo in Bundle
e viceversa.
Quando il caricatore crea l'elenco di MediaItems
, lo passa al VideoListAdapter
, che a sua volta presenta l'elenco MediaItems
nel file VideoBrowserFragment
. L'utente visualizza un elenco di miniature di ogni video con una breve descrizione. Quando viene selezionato un elemento, il valore MediaItem
corrispondente viene convertito in Bundle
e trasmesso in LocalPlayerActivity
.
LocalPlayerActivity
Questa attività mostra i metadati relativi a un determinato video e consente all'utente di riprodurlo localmente sul dispositivo mobile.
L'attività ospita una VideoView
, alcuni controlli multimediali e un'area di testo per mostrare la descrizione del video selezionato. Il player copre la parte superiore dello schermo, lasciando spazio alla descrizione dettagliata del video sottostante. L'utente può riprodurre/mettere in pausa i video oppure cercare la riproduzione locale dei video.
Dipendenze
Poiché stiamo utilizzando AppCompatActivity
, abbiamo bisogno della libreria di assistenza AppCompat. Per gestire l'elenco dei video e recuperare in modo asincrono le immagini da inserire nell'elenco, utilizziamo la raccolta Volley.
Domande frequenti
5. Aggiunta del pulsante Trasmetti
Un'applicazione compatibile con Google Cast mostra il pulsante Trasmetti in ogni sua attività. Se fai clic sul pulsante Trasmetti, viene visualizzato un elenco di dispositivi di trasmissione selezionabili dall'utente. Se l'utente stava riproducendo contenuti localmente sul dispositivo mittente, la selezione di un dispositivo di trasmissione avvia o riprende la riproduzione su tale dispositivo. Durante una sessione di trasmissione, l'utente può fare clic sul pulsante Trasmetti e interrompere la trasmissione dell'applicazione al dispositivo di trasmissione in qualsiasi momento. L'utente deve essere in grado di connettersi o disconnettersi dal dispositivo di trasmissione durante qualsiasi attività dell'applicazione, come descritto nell'elenco di controllo per la progettazione di Google Cast.
Dipendenze
Aggiorna il file build.gradle dell'app in modo che includa le dipendenze di libreria necessarie:
dependencies {
implementation 'androidx.appcompat:appcompat:1.5.0'
implementation 'androidx.mediarouter:mediarouter:1.3.1'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'com.google.android.gms:play-services-cast-framework:21.1.0'
implementation 'com.android.volley:volley:1.2.1'
implementation "androidx.core:core-ktx:1.8.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
Sincronizza il progetto per confermare che le build del progetto non presenti errori.
Inizializzazione
Il framework Cast ha un oggetto singleton globale, CastContext
, che coordina tutte le interazioni di Cast.
Devi implementare l'interfaccia OptionsProvider
per fornire CastOptions
necessario per inizializzare il singleton CastContext
. L'opzione più importante è l'ID applicazione del destinatario, che viene utilizzato per filtrare i risultati di rilevamento del dispositivo di trasmissione e per avviare l'applicazione del ricevitore quando viene avviata una sessione di trasmissione.
Quando sviluppi la tua app compatibile con Google Cast, devi registrarti come sviluppatore di Google Cast e ottenere un ID applicazione per l'app. Per questo codelab, utilizzeremo un ID app di esempio.
Aggiungi il nuovo file CastOptionsProvider.kt
seguente al pacchetto com.google.sample.cast.refplayer
del progetto:
package com.google.sample.cast.refplayer
import android.content.Context
import com.google.android.gms.cast.framework.OptionsProvider
import com.google.android.gms.cast.framework.CastOptions
import com.google.android.gms.cast.framework.SessionProvider
class CastOptionsProvider : OptionsProvider {
override fun getCastOptions(context: Context): CastOptions {
return CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.build()
}
override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
return null
}
}
Ora dichiara OptionsProvider
nel tag "application
" del file AndroidManifest.xml
dell'app:
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.sample.cast.refplayer.CastOptionsProvider" />
Inizializza lentamente CastContext
nel metodo onCreate VideoBrowserActivity
:
import com.google.android.gms.cast.framework.CastContext
private var mCastContext: CastContext? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.video_browser)
setupActionBar()
mCastContext = CastContext.getSharedInstance(this)
}
Aggiungi la stessa logica di inizializzazione a LocalPlayerActivity
.
Pulsante Trasmetti
Ora che il CastContext
è stato inizializzato, dobbiamo aggiungere il pulsante Trasmetti per consentire all'utente di selezionare un dispositivo di trasmissione. Il pulsante Trasmetti è implementato da MediaRouteButton
nella raccolta di assistenza di MediaRouter. Come per qualsiasi icona di azione che puoi aggiungere alla tua attività (utilizzando una ActionBar
o una Toolbar
), devi prima aggiungere la voce di menu corrispondente al menu.
Modifica il file res/menu/browse.xml
e aggiungi la voce MediaRouteActionProvider
nel menu prima della voce di impostazioni:
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always"/>
Esegui l'override del metodo onCreateOptionsMenu()
di VideoBrowserActivity
utilizzando CastButtonFactory
per collegare MediaRouteButton
al framework di trasmissione:
import com.google.android.gms.cast.framework.CastButtonFactory
private var mediaRouteMenuItem: MenuItem? = null
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.browse, menu)
mediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu,
R.id.media_route_menu_item)
return true
}
Esegui l'override di onCreateOptionsMenu
in LocalPlayerActivity
in modo simile.
Fai clic sul pulsante Esegui per eseguire l'app sul tuo dispositivo mobile. Dovresti vedere un pulsante Trasmetti nella barra delle azioni dell'app e quando lo fai clic vengono elencati i dispositivi di trasmissione sulla tua rete locale. Il rilevamento dei dispositivi è gestito automaticamente da CastContext
. Seleziona il tuo dispositivo di trasmissione: l'app ricevitore di esempio verrà caricata sul dispositivo di trasmissione. Puoi spostarti tra l'attività di navigazione e l'attività del player locale e lo stato del pulsante Trasmetti viene mantenuto sincronizzato.
Non abbiamo ancora supportato la riproduzione di contenuti multimediali, quindi non puoi ancora riprodurre video sul dispositivo di trasmissione. Fai clic sul pulsante Trasmetti per disconnetterti.
6. Trasmettere contenuti video
Estenderemo l'app di esempio in modo da riprodurre i video anche da remoto su un dispositivo di trasmissione. Per farlo, dobbiamo ascoltare i vari eventi generati dal framework di Cast.
Trasmissione di contenuti multimediali
A livello generale, se vuoi riprodurre un contenuto multimediale su un dispositivo di trasmissione, devi procedere nel seguente modo:
- Crea un oggetto
MediaInfo
che modella un elemento multimediale. - Connettiti al dispositivo di trasmissione e avvia l'applicazione del ricevitore.
- Carica l'oggetto
MediaInfo
nel ricevitore e riproduci i contenuti. - Tieni traccia dello stato dei contenuti multimediali.
- Invia i comandi di riproduzione al destinatario in base alle interazioni dell'utente.
Abbiamo già eseguito il passaggio 2 nella sezione precedente. Il passaggio 3 è facile da eseguire con il framework di trasmissione. Il passaggio 1 equivale a mappare un oggetto a un altro; MediaInfo
è qualcosa che il framework di trasmissione comprende, mentre MediaItem
è l'incapsulamento della nostra app per un elemento multimediale; possiamo mappare facilmente un MediaItem
a un MediaInfo
.
L'app di esempio LocalPlayerActivity
fa già una distinzione tra riproduzione locale e remota utilizzando questa enumerazione:
private var mLocation: PlaybackLocation? = null
enum class PlaybackLocation {
LOCAL, REMOTE
}
enum class PlaybackState {
PLAYING, PAUSED, BUFFERING, IDLE
}
In questo codelab non è importante capire esattamente come funziona l'intera logica del player di esempio. È importante capire che il media player dell'app dovrà essere modificato per tenere conto delle due posizioni di riproduzione in modo simile.
Al momento il player locale è sempre nello stato di riproduzione locale perché non sa ancora nulla sugli stati di trasmissione. Dobbiamo aggiornare l'interfaccia utente in base alle transizioni di stato che avvengono nel framework di trasmissione. Ad esempio, se iniziamo a trasmettere, dobbiamo interrompere la riproduzione locale e disattivare alcuni controlli. Analogamente, se interrompiamo la trasmissione mentre ci troviamo in questa attività, dobbiamo passare alla riproduzione locale. Per farlo, dobbiamo ascoltare i vari eventi generati dal framework di Cast.
Gestione delle sessioni di trasmissione
Per il framework di trasmissione, una sessione di trasmissione combina i passaggi per la connessione a un dispositivo, l'avvio (o l'unione), la connessione a un'applicazione ricevitore e l'inizializzazione di un canale di controllo multimediale, se opportuno. Il canale di controllo dei contenuti multimediali è il modo in cui il framework di trasmissione invia e riceve messaggi dal media player del ricevitore.
La sessione di trasmissione viene avviata automaticamente quando l'utente seleziona un dispositivo dal pulsante Trasmetti e viene interrotta automaticamente quando l'utente si disconnette. Anche la riconnessione a una sessione di ricezione a causa di problemi di rete viene gestita automaticamente dall'SDK Cast.
Aggiungiamo SessionManagerListener
a LocalPlayerActivity
:
import com.google.android.gms.cast.framework.CastSession
import com.google.android.gms.cast.framework.SessionManagerListener
...
private var mSessionManagerListener: SessionManagerListener<CastSession>? = null
private var mCastSession: CastSession? = null
...
private fun setupCastListener() {
mSessionManagerListener = object : SessionManagerListener<CastSession> {
override fun onSessionEnded(session: CastSession, error: Int) {
onApplicationDisconnected()
}
override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) {
onApplicationConnected(session)
}
override fun onSessionResumeFailed(session: CastSession, error: Int) {
onApplicationDisconnected()
}
override fun onSessionStarted(session: CastSession, sessionId: String) {
onApplicationConnected(session)
}
override fun onSessionStartFailed(session: CastSession, error: Int) {
onApplicationDisconnected()
}
override fun onSessionStarting(session: CastSession) {}
override fun onSessionEnding(session: CastSession) {}
override fun onSessionResuming(session: CastSession, sessionId: String) {}
override fun onSessionSuspended(session: CastSession, reason: Int) {}
private fun onApplicationConnected(castSession: CastSession) {
mCastSession = castSession
if (null != mSelectedMedia) {
if (mPlaybackState == PlaybackState.PLAYING) {
mVideoView!!.pause()
loadRemoteMedia(mSeekbar!!.progress, true)
return
} else {
mPlaybackState = PlaybackState.IDLE
updatePlaybackLocation(PlaybackLocation.REMOTE)
}
}
updatePlayButton(mPlaybackState)
invalidateOptionsMenu()
}
private fun onApplicationDisconnected() {
updatePlaybackLocation(PlaybackLocation.LOCAL)
mPlaybackState = PlaybackState.IDLE
mLocation = PlaybackLocation.LOCAL
updatePlayButton(mPlaybackState)
invalidateOptionsMenu()
}
}
}
Nell'attività di LocalPlayerActivity
, vorremmo ricevere informazioni quando ci connetteremo o disconnetteremo il dispositivo di trasmissione in modo da poter passare da o verso il player locale. Tieni presente che la connettività può essere interrotta non solo dall'istanza dell'applicazione in esecuzione sul tuo dispositivo mobile, ma anche da un'altra istanza della tua applicazione (o di un'altra) in esecuzione su un dispositivo mobile diverso.
La sessione attualmente attiva è accessibile come SessionManager.getCurrentSession()
. Le sessioni vengono create e eliminate automaticamente in risposta alle interazioni degli utenti con le finestre di dialogo di trasmissione.
Dobbiamo registrare il nostro listener di sessione e inizializzare alcune variabili che utilizzeremo nell'attività. Cambia il metodo onCreate
di LocalPlayerActivity
in:
import com.google.android.gms.cast.framework.CastContext
...
private var mCastContext: CastContext? = null
...
override fun onCreate(savedInstanceState: Bundle?) {
...
mCastContext = CastContext.getSharedInstance(this)
mCastSession = mCastContext!!.sessionManager.currentCastSession
setupCastListener()
...
loadViews()
...
val bundle = intent.extras
if (bundle != null) {
....
if (shouldStartPlayback) {
....
} else {
if (mCastSession != null && mCastSession!!.isConnected()) {
updatePlaybackLocation(PlaybackLocation.REMOTE)
} else {
updatePlaybackLocation(PlaybackLocation.LOCAL)
}
mPlaybackState = PlaybackState.IDLE
updatePlayButton(mPlaybackState)
}
}
...
}
Caricamento contenuti multimediali in corso...
Nell'SDK Cast, RemoteMediaClient
fornisce una serie di utili API per la gestione della riproduzione remota di contenuti multimediali sul ricevitore. Per un CastSession
che supporta la riproduzione di contenuti multimediali, l'SDK crea automaticamente un'istanza di RemoteMediaClient
. È possibile accedervi chiamando il metodo getRemoteMediaClient()
sull'istanza CastSession
. Aggiungi i seguenti metodi a LocalPlayerActivity
per caricare il video attualmente selezionato sul ricevitore:
import com.google.android.gms.cast.framework.media.RemoteMediaClient
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaLoadOptions
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.common.images.WebImage
import com.google.android.gms.cast.MediaLoadRequestData
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
if (mCastSession == null) {
return
}
val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return
remoteMediaClient.load( MediaLoadRequestData.Builder()
.setMediaInfo(buildMediaInfo())
.setAutoplay(autoPlay)
.setCurrentTime(position.toLong()).build())
}
private fun buildMediaInfo(): MediaInfo? {
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
mSelectedMedia?.studio?.let { movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, it) }
mSelectedMedia?.title?.let { movieMetadata.putString(MediaMetadata.KEY_TITLE, it) }
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(0))))
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(1))))
return mSelectedMedia!!.url?.let {
MediaInfo.Builder(it)
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType("videos/mp4")
.setMetadata(movieMetadata)
.setStreamDuration((mSelectedMedia!!.duration * 1000).toLong())
.build()
}
}
Ora aggiorna i vari metodi esistenti per utilizzare la logica della sessione di trasmissione in modo da supportare la riproduzione remota:
private fun play(position: Int) {
startControllersTimer()
when (mLocation) {
PlaybackLocation.LOCAL -> {
mVideoView!!.seekTo(position)
mVideoView!!.start()
}
PlaybackLocation.REMOTE -> {
mPlaybackState = PlaybackState.BUFFERING
updatePlayButton(mPlaybackState)
//seek to a new position within the current media item's new position
//which is in milliseconds from the beginning of the stream
mCastSession!!.remoteMediaClient?.seek(position.toLong())
}
else -> {}
}
restartTrickplayTimer()
}
private fun togglePlayback() {
...
PlaybackState.IDLE -> when (mLocation) {
...
PlaybackLocation.REMOTE -> {
if (mCastSession != null && mCastSession!!.isConnected) {
loadRemoteMedia(mSeekbar!!.progress, true)
}
}
else -> {}
}
...
}
override fun onPause() {
...
mCastContext!!.sessionManager.removeSessionManagerListener(
mSessionManagerListener!!, CastSession::class.java)
}
override fun onResume() {
Log.d(TAG, "onResume() was called")
mCastContext!!.sessionManager.addSessionManagerListener(
mSessionManagerListener!!, CastSession::class.java)
if (mCastSession != null && mCastSession!!.isConnected) {
updatePlaybackLocation(PlaybackLocation.REMOTE)
} else {
updatePlaybackLocation(PlaybackLocation.LOCAL)
}
super.onResume()
}
Per il metodo updatePlayButton
, modifica il valore della variabile isConnected
:
private fun updatePlayButton(state: PlaybackState?) {
...
val isConnected = (mCastSession != null
&& (mCastSession!!.isConnected || mCastSession!!.isConnecting))
...
}
Ora, fai clic sul pulsante Esegui per eseguire l'app sul tuo dispositivo mobile. Collegati al tuo dispositivo di trasmissione e inizia a riprodurre un video. Dovresti vedere il video in riproduzione sul ricevitore.
7. Mini controller
L'elenco di controllo della progettazione di Cast richiede che tutte le app Cast forniscano un mini controller che viene visualizzato quando l'utente esce dalla pagina di contenuti corrente. Il mini controller fornisce accesso immediato e un promemoria visibile per la sessione di trasmissione corrente.
L'SDK Cast fornisce una visualizzazione personalizzata, MiniControllerFragment
, che può essere aggiunta al file di layout dell'app relativo alle attività in cui vuoi mostrare il mini controller.
Aggiungi la seguente definizione di frammento in fondo sia a res/layout/player_activity.xml
che a res/layout/video_browser.xml
:
<fragment
android:id="@+id/castMiniController"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment"/>
Fai clic sul pulsante Esegui per eseguire l'app e trasmettere un video. Quando inizia la riproduzione sul ricevitore, dovresti vedere il mini controller nella parte inferiore di ogni attività. Puoi controllare la riproduzione remota utilizzando il mini controller. Se passi dall'attività di navigazione all'attività locale del player, lo stato del mini controller dovrebbe rimanere sincronizzato con lo stato di riproduzione dei contenuti multimediali del ricevitore.
8. Notifica e schermata di blocco
L'elenco di controllo per la progettazione di Google Cast richiede che un'app del mittente implementi i controlli multimediali da una notifica e dalla schermata di blocco.
L'SDK Cast fornisce un elemento MediaNotificationService
per aiutare l'app del mittente a creare controlli multimediali per le notifiche e la schermata di blocco. Il servizio viene unito automaticamente al file manifest della tua app tramite Gradle.
Il dispositivo MediaNotificationService
verrà eseguito in background durante la trasmissione e mostrerà una notifica con una miniatura dell'immagine e metadati relativi all'elemento di trasmissione corrente, un pulsante di riproduzione/pausa e un pulsante di interruzione.
I controlli delle notifiche e della schermata di blocco possono essere attivati con il CastOptions
durante l'inizializzazione di CastContext
. I controlli multimediali per le notifiche e la schermata di blocco sono attivi per impostazione predefinita. La funzionalità di schermata di blocco è attiva se le notifiche sono attive.
Modifica CastOptionsProvider
e cambia l'implementazione getCastOptions
in modo che corrisponda a questo codice:
import com.google.android.gms.cast.framework.media.CastMediaOptions
import com.google.android.gms.cast.framework.media.NotificationOptions
override fun getCastOptions(context: Context): CastOptions {
val notificationOptions = NotificationOptions.Builder()
.setTargetActivityClassName(VideoBrowserActivity::class.java.name)
.build()
val mediaOptions = CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
.build()
return CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.setCastMediaOptions(mediaOptions)
.build()
}
Fai clic sul pulsante Esegui per eseguire l'app sul tuo dispositivo mobile. Trasmetti un video e esci dall'app di esempio. Dovresti ricevere una notifica relativa al video attualmente in riproduzione sul ricevitore. Blocca il dispositivo mobile e la schermata di blocco ora dovrebbe visualizzare i controlli per la riproduzione di contenuti multimediali sul dispositivo di trasmissione.
9. Overlay introduttivo
L'elenco di controllo della progettazione di Google Cast richiede che un'app del mittente presenti il pulsante Trasmetti agli utenti esistenti per comunicare loro che ora l'app del mittente supporta la trasmissione e aiuta anche gli utenti che non hanno mai utilizzato Google Cast.
L'SDK Cast fornisce una visualizzazione personalizzata, IntroductoryOverlay
, che può essere utilizzata per evidenziare il pulsante Trasmetti quando viene mostrato agli utenti per la prima volta. Aggiungi il seguente codice a VideoBrowserActivity
:
import com.google.android.gms.cast.framework.IntroductoryOverlay
import android.os.Looper
private var mIntroductoryOverlay: IntroductoryOverlay? = null
private fun showIntroductoryOverlay() {
mIntroductoryOverlay?.remove()
if (mediaRouteMenuItem?.isVisible == true) {
Looper.myLooper().run {
mIntroductoryOverlay = com.google.android.gms.cast.framework.IntroductoryOverlay.Builder(
this@VideoBrowserActivity, mediaRouteMenuItem!!)
.setTitleText("Introducing Cast")
.setSingleTime()
.setOnOverlayDismissedListener(
object : IntroductoryOverlay.OnOverlayDismissedListener {
override fun onOverlayDismissed() {
mIntroductoryOverlay = null
}
})
.build()
mIntroductoryOverlay!!.show()
}
}
}
Ora aggiungi un CastStateListener
e chiama il metodo showIntroductoryOverlay
quando è disponibile un dispositivo di trasmissione modificando il metodo onCreate
e sostituisci i metodi onResume
e onPause
in modo che corrispondano ai seguenti criteri:
import com.google.android.gms.cast.framework.CastState
import com.google.android.gms.cast.framework.CastStateListener
private var mCastStateListener: CastStateListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.video_browser)
setupActionBar()
mCastStateListener = object : CastStateListener {
override fun onCastStateChanged(newState: Int) {
if (newState != CastState.NO_DEVICES_AVAILABLE) {
showIntroductoryOverlay()
}
}
}
mCastContext = CastContext.getSharedInstance(this)
}
override fun onResume() {
super.onResume()
mCastContext?.addCastStateListener(mCastStateListener!!)
}
override fun onPause() {
super.onPause()
mCastContext?.removeCastStateListener(mCastStateListener!!)
}
Cancella i dati dell'app o rimuovi l'app dal dispositivo. Successivamente, fai clic sul pulsante Esegui per eseguire l'app sul tuo dispositivo mobile. Dovresti vedere l'overlay introduttivo (cancella i dati dell'app se l'overlay non viene visualizzato).
10. Controller espanso
L'elenco di controllo per la progettazione di Google Cast richiede che un'app del mittente fornisca un controller espanso per i contenuti multimediali trasmessi. Il controller espanso è una versione a schermo intero del mini controller.
L'SDK Cast fornisce un widget per il controller espanso chiamato ExpandedControllerActivity
. È una classe astratta che devi sottoclasse per aggiungere un pulsante Trasmetti.
Innanzitutto, crea un nuovo file di risorse del menu, denominato expanded_controller.xml
, affinché il controller espanso fornisca il pulsante Trasmetti:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always"/>
</menu>
Crea un nuovo pacchetto expandedcontrols
nel pacchetto com.google.sample.cast.refplayer
. Poi, crea un nuovo file denominato ExpandedControlsActivity.kt
nel pacchetto com.google.sample.cast.refplayer.expandedcontrols
.
package com.google.sample.cast.refplayer.expandedcontrols
import android.view.Menu
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
import com.google.sample.cast.refplayer.R
import com.google.android.gms.cast.framework.CastButtonFactory
class ExpandedControlsActivity : ExpandedControllerActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.expanded_controller, menu)
CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
return true
}
}
Ora dichiara ExpandedControlsActivity
nel tag AndroidManifest.xml
all'interno del tag application
sopra OPTIONS_PROVIDER_CLASS_NAME
:
<application>
...
<activity
android:name="com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/Theme.CastVideosDark"
android:screenOrientation="portrait"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.google.sample.cast.refplayer.VideoBrowserActivity"/>
</activity>
...
</application>
Modifica CastOptionsProvider
e NotificationOptions
e CastMediaOptions
per impostare l'attività target su ExpandedControlsActivity
:
import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity
override fun getCastOptions(context: Context): CastOptions {
val notificationOptions = NotificationOptions.Builder()
.setTargetActivityClassName(ExpandedControlsActivity::class.java.name)
.build()
val mediaOptions = CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
.setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name)
.build()
return CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_id))
.setCastMediaOptions(mediaOptions)
.build()
}
Aggiorna il metodo loadRemoteMedia
LocalPlayerActivity
per visualizzare ExpandedControlsActivity
quando viene caricato il file multimediale remoto:
import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
if (mCastSession == null) {
return
}
val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return
remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() {
override fun onStatusUpdated() {
val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java)
startActivity(intent)
remoteMediaClient.unregisterCallback(this)
}
})
remoteMediaClient.load(MediaLoadRequestData.Builder()
.setMediaInfo(buildMediaInfo())
.setAutoplay(autoPlay)
.setCurrentTime(position.toLong()).build())
}
Fai clic sul pulsante Esegui per eseguire l'app sul tuo dispositivo mobile e trasmettere un video. Dovresti vedere il controller espanso. Torna all'elenco dei video e quando fai clic sul mini controller, il controller espanso verrà caricato di nuovo. Esci dall'app per visualizzare la notifica. Fai clic sull'immagine della notifica per caricare il controller espanso.
11. Aggiungi il supporto di Cast Connect
La libreria di Cast Connect consente alle applicazioni dei mittenti esistenti di comunicare con le applicazioni Android TV tramite il protocollo Cast. Cast Connect si basa sull'infrastruttura di Google Cast e la tua app Android TV funge da ricevitore.
Dipendenze
Nota: per l'implementazione di Cast Connect, play-services-cast-framework
deve essere 19.0.0
o superiore.
LaunchOptions
Per poter avviare l'applicazione Android TV, nota anche come Ricevitore Android, dobbiamo impostare il flag setAndroidReceiverCompatible
su true nell'oggetto LaunchOptions
. Questo oggetto LaunchOptions
determina il modo in cui il ricevitore viene avviato e viene trasmesso al CastOptions
restituito dalla classe CastOptionsProvider
. Se imposti il flag sopra indicato su false
, verrà avviato il ricevitore web per l'ID app definito nella Console per gli sviluppatori di Google Cast.
Nel file CastOptionsProvider.kt
aggiungi quanto segue al metodo getCastOptions
:
import com.google.android.gms.cast.LaunchOptions
...
val launchOptions = LaunchOptions.Builder()
.setAndroidReceiverCompatible(true)
.build()
return new CastOptions.Builder()
.setLaunchOptions(launchOptions)
...
.build()
Imposta credenziali di avvio
Sul lato del mittente, puoi specificare CredentialsData
per rappresentare chi partecipa alla sessione. credentials
è una stringa che può essere definita dall'utente, a condizione che l'app ATV sia in grado di comprenderla. Il CredentialsData
viene trasmesso all'app Android TV soltanto durante l'ora di avvio o di iscrizione. Se lo imposti di nuovo mentre il dispositivo è connesso, non verrà trasmesso all'app Android TV.
Per impostare le credenziali di lancio, CredentialsData
deve essere definito e trasmesso all'oggetto LaunchOptions
. Aggiungi il seguente codice al metodo getCastOptions
nel file CastOptionsProvider.kt
:
import com.google.android.gms.cast.CredentialsData
...
val credentialsData = CredentialsData.Builder()
.setCredentials("{\"userId\": \"abc\"}")
.build()
val launchOptions = LaunchOptions.Builder()
...
.setCredentialsData(credentialsData)
.build()
Imposta credenziali su LoadRequest
Nel caso in cui l'app Web Ricevir e l'app Android TV gestiscano credentials
in modo diverso, potresti dover definire credentials
separati per ciascuna. A questo scopo, aggiungi il seguente codice nel file LocalPlayerActivity.kt
nella funzione loadRemoteMedia
:
remoteMediaClient.load(MediaLoadRequestData.Builder()
...
.setCredentials("user-credentials")
.setAtvCredentials("atv-user-credentials")
.build())
A seconda dell'app del destinatario a cui il mittente trasmette, l'SDK gestisce automaticamente le credenziali da utilizzare per la sessione corrente.
Test di Cast Connect
Procedura per installare l'APK di Android TV su Chromecast con Google TV
- Trova l'indirizzo IP del tuo dispositivo Android TV. Di solito questa opzione è disponibile in Impostazioni > Rete e internet > (Nome della rete a cui è connesso il dispositivo). Sulla destra sono visualizzati i dettagli e l'IP del dispositivo sulla rete.
- Usa l'indirizzo IP del dispositivo per collegarti tramite ADB utilizzando il terminale:
$ adb connect <device_ip_address>:5555
- Nella finestra del terminale, vai alla cartella di primo livello per trovare gli esempi di codelab che hai scaricato all'inizio di questo codelab. Ad esempio:
$ cd Desktop/android_codelab_src
- Installa il file .apk in questa cartella su Android TV eseguendo:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- Ora dovresti riuscire a vedere un'app chiamata Trasmetti video nel menu Le tue app sul dispositivo Android TV.
- Torna al tuo progetto Android Studio e fai clic sul pulsante Esegui per installare ed eseguire l'app del mittente sul tuo dispositivo mobile fisico. Nell'angolo in alto a destra, fai clic sull'icona Trasmetti e seleziona il tuo dispositivo Android TV tra le opzioni disponibili. Ora dovresti visualizzare l'app Android TV avviata sul dispositivo Android TV e la riproduzione di un video dovrebbe consentirti di controllare la riproduzione del video utilizzando il telecomando di Android TV.
12. Personalizza i widget di trasmissione
Puoi personalizzare i widget di trasmissione impostando i colori, assegnando uno stile ai pulsanti, al testo e all'aspetto delle miniature e scegliendo i tipi di pulsanti da visualizzare.
Aggiorna res/values/styles_castvideo.xml
<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="mediaRouteTheme">@style/CustomMediaRouterTheme</item>
<item name="castIntroOverlayStyle">@style/CustomCastIntroOverlay</item>
<item name="castMiniControllerStyle">@style/CustomCastMiniController</item>
<item name="castExpandedControllerStyle">@style/CustomCastExpandedController</item>
<item name="castExpandedControllerToolbarStyle">
@style/ThemeOverlay.AppCompat.ActionBar
</item>
...
</style>
Dichiara i seguenti temi personalizzati:
<!-- Customize Cast Button -->
<style name="CustomMediaRouterTheme" parent="Theme.MediaRouter">
<item name="mediaRouteButtonStyle">@style/CustomMediaRouteButtonStyle</item>
</style>
<style name="CustomMediaRouteButtonStyle" parent="Widget.MediaRouter.Light.MediaRouteButton">
<item name="mediaRouteButtonTint">#EEFF41</item>
</style>
<!-- Customize Introductory Overlay -->
<style name="CustomCastIntroOverlay" parent="CastIntroOverlay">
<item name="castButtonTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Button</item>
<item name="castTitleTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Title</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Button" parent="android:style/TextAppearance">
<item name="android:textColor">#FFFFFF</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Title" parent="android:style/TextAppearance.Large">
<item name="android:textColor">#FFFFFF</item>
</style>
<!-- Customize Mini Controller -->
<style name="CustomCastMiniController" parent="CastMiniController">
<item name="castShowImageThumbnail">true</item>
<item name="castTitleTextAppearance">@style/TextAppearance.AppCompat.Subhead</item>
<item name="castSubtitleTextAppearance">@style/TextAppearance.AppCompat.Caption</item>
<item name="castBackground">@color/accent</item>
<item name="castProgressBarColor">@color/orange</item>
</style>
<!-- Customize Expanded Controller -->
<style name="CustomCastExpandedController" parent="CastExpandedController">
<item name="castButtonColor">#FFFFFF</item>
<item name="castPlayButtonDrawable">@drawable/cast_ic_expanded_controller_play</item>
<item name="castPauseButtonDrawable">@drawable/cast_ic_expanded_controller_pause</item>
<item name="castStopButtonDrawable">@drawable/cast_ic_expanded_controller_stop</item>
</style>
13. Congratulazioni
Ora sai come attivare la trasmissione di un'app video utilizzando i widget dell'SDK Cast su Android.
Per maggiori dettagli, consulta la guida per gli sviluppatori Android Sender.