1. Panoramica
Questo codelab ti insegna a modificare un'app Android TV esistente per supportare la trasmissione e la comunicazione dalle app di trasmissione esistenti.
Che cosa sono Google Cast e Cast Connect?
Google Cast consente agli utenti di trasmettere contenuti da un dispositivo mobile a una TV. Una sessione tipica di Google Cast è composta da due componenti: un mittente e un'applicazione ricevitore. Le applicazioni di invio, come un'app mobile o un sito web come YouTube.com, avviano e controllano la riproduzione di un'applicazione di ricezione di Google Cast. Le applicazioni di ricezione di trasmissione sono app HTML 5 che vengono eseguite su Chromecast e Android TV.
Quasi tutto lo stato di una sessione di trasmissione viene archiviato nell'applicazione di ricezione. Quando lo stato si aggiorna, ad esempio se viene caricato un nuovo elemento multimediale, viene trasmesso un stato multimediale a tutti i mittenti. Queste trasmissioni contengono lo stato attuale della sessione di trasmissione. Le applicazioni di invio utilizzano questo stato dei contenuti multimediali per visualizzare le informazioni sulla riproduzione nella loro UI.
Cast Connect si basa su questa infrastruttura, con l'app Android TV che funge da ricevitore. La raccolta Cast Connect consente all'app Android TV di ricevere messaggi e trasmettere lo stato dei contenuti multimediali come se si trattasse di un'applicazione di ricevitore di trasmissione.
Cosa realizzeremo?
Al termine di questo codelab, potrai utilizzare le app di trasmissione di Cast per trasmettere video a un'app Android TV. L'app Android TV può anche comunicare con le app di trasmissione tramite il protocollo Cast.
Obiettivi didattici
- Come aggiungere la libreria Cast Connect a un'app ATV di esempio.
- Come connettere un dispositivo di trasmissione di contenuti e avviare l'app ATV.
- Come avviare la riproduzione di contenuti multimediali sull'app ATV da un'app del mittente di trasmissione.
- Come inviare lo stato dei contenuti multimediali dall'app ATV alle app di invio di Cast.
Che cosa ti serve
- L'SDK Android più recente.
- La versione più recente di Android Studio. Nello specifico,
Chipmunk | 2021.2.1
o versioni successive. - Un dispositivo Android TV su cui sono state attivate le Opzioni sviluppatore e il debug USB.
- Uno smartphone Android su cui sono state attivate le opzioni sviluppatore e il debug USB.
- Un cavo dati USB per collegare lo smartphone Android e i dispositivi Android TV al computer di sviluppo.
- Conoscenza di base dello sviluppo di applicazioni Android utilizzando Kotlin.
2. recupera il codice campione
Puoi scaricare tutto il codice campione sul tuo computer...
ed espandi il file ZIP scaricato.
3. Esegui l'app di esempio
Innanzitutto, vediamo come appare l'app di esempio completata. L'app per Android TV utilizza l'interfaccia utente Leanback e un video player di base. L'utente può selezionare un video da un elenco, che viene poi riprodotto sulla TV. Con l'app mittente per dispositivi mobili allegata, un utente può anche trasmettere un video all'app per Android TV.
Registra i dispositivi degli sviluppatori
Per attivare le funzionalità di Cast Connect per lo sviluppo di applicazioni, devi registrare il numero di serie del Chromecast integrato del dispositivo Android TV che intendi utilizzare nella Console per sviluppatori di Google Cast. Puoi trovare il numero di serie in Impostazioni > Preferenze del dispositivo > Chromecast integrato > Numero di serie su Android TV. Tieni presente che è diverso dal numero di serie del dispositivo fisico e deve essere ottenuto dal metodo descritto sopra.
Per motivi di sicurezza, senza registrazione Cast Connect funziona solo per le app installate dal Google Play Store. Riavvia il dispositivo 15 minuti dopo l'avvio della procedura di registrazione.
Installare l'app mittente per Android
Per testare l'invio di richieste da un dispositivo mobile, abbiamo fornito una semplice applicazione di invio denominata Trasmetti video come file mobile-sender-0629.apk
nel download del file ZIP del codice sorgente. Utilizzeremo ADB per installare l'APK. Se hai già installato una versione diversa di Trasferisci video, disinstallala da tutti i profili presenti sul dispositivo prima di continuare.
- Attiva le Opzioni sviluppatore e il debug USB sul tuo smartphone Android.
- Collega un cavo dati USB per collegare lo smartphone Android al computer di sviluppo.
- Installa
mobile-sender-0629.apk
sullo smartphone Android.
- Puoi trovare l'app di invio Trasmetti video sul tuo smartphone Android.
Installa l'app per Android TV
Le istruzioni riportate di seguito descrivono 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 il progetto con i file Gradle.
- Attiva le opzioni sviluppatore e il debug USB sul dispositivo Android TV.
- ADB si connette al dispositivo Android TV, che dovrebbe essere visualizzato in Android Studio.
- Fai clic sul pulsante Esegui. Dopo alcuni secondi, dovresti vedere l'app ATV denominata Cast Connect Codelab.
Let's play Cast Connect con l'app ATV
- Vai alla schermata Home di Android TV.
- Apri l'app di invio di video di trasmissione dal tuo smartphone Android. Fai clic sul pulsante Trasmetti e seleziona il tuo dispositivo ATV.
- L'app ATV di Codelab Cast Connect verrà avviata sull'ATV e il pulsante Trasmetti nel dispositivo di invio indicherà che è connesso .
- Seleziona un video dall'app ATV per avviare la riproduzione sul tuo ATV.
- Sullo smartphone, nella parte inferiore dell'app mittente è ora visibile un mini controller. Puoi utilizzare il pulsante di riproduzione/messa in pausa per controllare la riproduzione.
- Seleziona un video dal cellulare e riproducilo. Il video verrà riprodotto sul tuo ATV e il controller espanso verrà visualizzato sul mittente del dispositivo mobile.
- Blocca il telefono e, quando lo sblocchi, dovresti vedere una notifica nella schermata di blocco per controllare la riproduzione dei contenuti multimediali o interrompere la trasmissione.
4. Prepara il progetto iniziale
Ora che abbiamo verificato l'integrazione di Cast Connect dell'app completata, dobbiamo aggiungere il supporto di Cast Connect all'app di avvio che hai scaricato. Ora puoi iniziare a sviluppare il progetto iniziale utilizzando Android Studio:
- Seleziona Importa progetto nella schermata di benvenuto o le opzioni di menu File > Nuovo > Importa progetto.
- Seleziona la directory
app-start
dalla cartella del codice di esempio e fai clic su OK. - Fai clic su File > Sincronizza il progetto con i file Gradle.
- Seleziona il dispositivo ATV e fai clic sul pulsante Esegui per eseguire l'app ed esplorare l'interfaccia utente.
Progettazione di app
L'app fornisce un elenco di video che l'utente può sfogliare. Gli utenti possono selezionare un video da riprodurre su Android TV. L'app è composta da due attività principali: MainActivity
e PlaybackActivity
.
MainActivity
Questa attività contiene un frammento (MainFragment
). L'elenco dei video e dei relativi metadati associati è configurato nella classe MovieList
e il metodo setupMovies()
viene chiamato per creare un elenco di oggetti Movie
.
Un oggetto Movie
rappresenta un'entità video con titolo, descrizione, miniature di immagini e URL del video. Ogni oggetto Movie
è associato a un CardPresenter
per presentare la miniatura del video con titolo e casa di produzione e viene passato a ArrayObjectAdapter
.
Quando viene selezionato un elemento, l'oggetto Movie
corrispondente viene passato a PlaybackActivity
.
PlaybackActivity
Questa attività contiene un frammento (PlaybackVideoFragment
) che ospita VideoView
con ExoPlayer
, alcuni controlli multimediali e un'area di testo per mostrare la descrizione del video selezionato e consentire all'utente di riprodurlo su Android TV. L'utente può utilizzare il telecomando per riprodurre/mettere in pausa o scorrere la riproduzione dei video.
Prerequisiti di Cast Connect
Cast Connect utilizza nuove versioni di Google Play Services che richiedono l'aggiornamento dell'app ATV per utilizzare lo spazio dei nomi AndroidX.
Per supportare Cast Connect nella tua app per Android TV, devi creare e supportare gli eventi da una sessione multimediale. La raccolta Cast Connect genera lo stato dei contenuti multimediali in base allo stato della sessione multimediale. La sessione multimediale viene utilizzata anche dalla libreria Cast Connect per segnalare quando ha ricevuto determinati messaggi da un mittente, ad esempio la messa in pausa.
5. Configurare il supporto di trasmissione
Dipendenze
Aggiorna il file build.gradle
dell'app in modo da includere le dipendenze delle librerie necessarie:
dependencies {
....
// Cast Connect libraries
implementation 'com.google.android.gms:play-services-cast-tv:20.0.0'
implementation 'com.google.android.gms:play-services-cast:21.1.0'
}
Sincronizza il progetto per verificare che le build vengano eseguite senza errori.
Inizializzazione
CastReceiverContext
è un oggetto singleton per coordinare tutte le interazioni di trasmissione. Devi implementare l'interfaccia ReceiverOptionsProvider
per fornire CastReceiverOptions
quando CastReceiverContext
viene inizializzato.
Crea il file CastReceiverOptionsProvider.kt
e aggiungi la seguente classe al progetto:
package com.google.sample.cast.castconnect
import android.content.Context
import com.google.android.gms.cast.tv.ReceiverOptionsProvider
import com.google.android.gms.cast.tv.CastReceiverOptions
class CastReceiverOptionsProvider : ReceiverOptionsProvider {
override fun getOptions(context: Context): CastReceiverOptions {
return CastReceiverOptions.Builder(context)
.setStatusText("Cast Connect Codelab")
.build()
}
}
Quindi specifica il provider di opzioni di destinazione all'interno del tag <application>
del file AndroidManifest.xml
dell'app:
<application>
...
<meta-data
android:name="com.google.android.gms.cast.tv.RECEIVER_OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.sample.cast.castconnect.CastReceiverOptionsProvider" />
</application>
Per connetterti all'app ATV dal tuo dispositivo di trasmissione di Cast, seleziona un'attività che vuoi avviare. In questo codelab, lanceremo il MainActivity
dell'app all'avvio di una sessione di trasmissione. Nel file AndroidManifest.xml
, aggiungi il filtro per intent di avvio in MainActivity
.
<activity android:name=".MainActivity">
...
<intent-filter>
<action android:name="com.google.android.gms.cast.tv.action.LAUNCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Ciclo di vita del contesto del ricevitore di trasmissione
Dovresti avviare CastReceiverContext
all'avvio dell'app e interrompere CastReceiverContext
quando l'app viene spostata in background. Ti consigliamo di utilizzare LifecycleObserver
dalla libreria androidx.lifecycle per gestire le chiamate a CastReceiverContext.start()
e CastReceiverContext.stop()
Apri il file MyApplication.kt
, inizializza il contesto di trasmissione chiamando initInstance()
nel metodo onCreate
dell'applicazione. Nella classe AppLifeCycleObserver
start()
il CastReceiverContext
quando l'applicazione viene ripresa e stop()
quando viene messa in pausa:
package com.google.sample.cast.castconnect
import com.google.android.gms.cast.tv.CastReceiverContext
...
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
CastReceiverContext.initInstance(this)
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver())
}
class AppLifecycleObserver : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
Log.d(LOG_TAG, "onResume")
CastReceiverContext.getInstance().start()
}
override fun onPause(owner: LifecycleOwner) {
Log.d(LOG_TAG, "onPause")
CastReceiverContext.getInstance().stop()
}
}
}
Collegamento di MediaSession a MediaManager
MediaManager
è una proprietà dell'oggetto singleton CastReceiverContext
, gestisce lo stato dei contenuti multimediali, gestisce l'intent di caricamento, traduce i messaggi dello spazio dei nomi multimediali dei mittenti in comandi multimediali e restituisce lo stato dei contenuti multimediali ai mittenti.
Quando crei un MediaSession
, devi anche fornire il token MediaSession
corrente a MediaManager
in modo che sappia dove inviare i comandi e recuperare lo stato di riproduzione dei contenuti multimediali. Nel file PlaybackVideoFragment.kt
, assicurati che MediaSession
sia stato inizializzato prima di impostare il token su MediaManager
.
import com.google.android.gms.cast.tv.CastReceiverContext
import com.google.android.gms.cast.tv.media.MediaManager
...
class PlaybackVideoFragment : VideoSupportFragment() {
private var castReceiverContext: CastReceiverContext? = null
...
private fun initializePlayer() {
if (mPlayer == null) {
...
mMediaSession = MediaSessionCompat(getContext(), LOG_TAG)
...
castReceiverContext = CastReceiverContext.getInstance()
if (castReceiverContext != null) {
val mediaManager: MediaManager = castReceiverContext!!.getMediaManager()
mediaManager.setSessionCompatToken(mMediaSession!!.getSessionToken())
}
}
}
}
Quando rilasci MediaSession
a causa di una riproduzione inattiva, devi impostare un token nullo su MediaManager
:
private fun releasePlayer() {
mMediaSession?.release()
castReceiverContext?.mediaManager?.setSessionCompatToken(null)
...
}
Esegui l'app di esempio
Fai clic sul pulsante Esegui per eseguire il deployment dell'app sul dispositivo ATV, chiudila e torna alla schermata Home di ATV. Sul dispositivo di invio, fai clic sul pulsante Trasmetti e seleziona il dispositivo ATV. Vedrai che l'app ATV è stata avviata sul dispositivo ATV e che lo stato del pulsante Trasmetti è Connesso.
6. Caricamento dei contenuti multimediali
Il comando di caricamento viene inviato tramite un'intenzione con il nome del pacchetto definito nella Console per gli sviluppatori. Devi aggiungere il seguente filtro per intent predefinito nella tua app per Android TV per specificare l'attività target che riceverà questo intent. Nel file AndroidManifest.xml
, aggiungi il filtro per intent di caricamento a PlayerActivity
:
<activity android:name="com.google.sample.cast.castconnect.PlaybackActivity"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.cast.tv.action.LOAD"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Gestione delle richieste di caricamento su Android TV
Ora che l'attività è configurata per ricevere questo intento contenente una richiesta di caricamento, dovremo gestirla.
L'app chiama un metodo privato chiamato processIntent
all'avvio dell'attività. Questo metodo contiene la logica per l'elaborazione degli intent in arrivo. Per gestire una richiesta di caricamento, modificheremo questo metodo e invieremo l'intent da elaborare ulteriormente chiamando il metodo onNewIntent
dell'istanza MediaManager
. Se MediaManager
rileva che l'intent è una richiesta di caricamento, estrae l'oggetto MediaLoadRequestData
dall'intent e richiama MediaLoadCommandCallback.onLoad()
. Modifica il metodo processIntent
nel file PlaybackVideoFragment.kt
per gestire l'intent contenente la richiesta di caricamento:
fun processIntent(intent: Intent?) {
val mediaManager: MediaManager = CastReceiverContext.getInstance().getMediaManager()
// Pass intent to Cast SDK
if (mediaManager.onNewIntent(intent)) {
return
}
// Clears all overrides in the modifier.
mediaManager.getMediaStatusModifier().clear()
// If the SDK doesn't recognize the intent, handle the intent with your own logic.
...
}
Successivamente estenderemo la classe astratta MediaLoadCommandCallback
che sostituirà il metodo onLoad()
chiamato da MediaManager
. Questo metodo riceve i dati della richiesta di caricamento e li converte in un oggetto Movie
. Dopo la conversione, il film viene riprodotto dal player locale. Il MediaManager
viene quindi aggiornato con il MediaLoadRequest
e trasmette il MediaStatus
ai mittenti collegati. Crea una classe privata nidificata denominata MyMediaLoadCommandCallback
nel file PlaybackVideoFragment.kt
:
import com.google.android.gms.cast.MediaLoadRequestData
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.cast.MediaError
import com.google.android.gms.cast.tv.media.MediaException
import com.google.android.gms.cast.tv.media.MediaCommandCallback
import com.google.android.gms.cast.tv.media.QueueUpdateRequestData
import com.google.android.gms.cast.tv.media.MediaLoadCommandCallback
import com.google.android.gms.tasks.Task
import com.google.android.gms.tasks.Tasks
import android.widget.Toast
...
private inner class MyMediaLoadCommandCallback : MediaLoadCommandCallback() {
override fun onLoad(
senderId: String?, mediaLoadRequestData: MediaLoadRequestData): Task<MediaLoadRequestData> {
Toast.makeText(activity, "onLoad()", Toast.LENGTH_SHORT).show()
return if (mediaLoadRequestData == null) {
// Throw MediaException to indicate load failure.
Tasks.forException(MediaException(
MediaError.Builder()
.setDetailedErrorCode(MediaError.DetailedErrorCode.LOAD_FAILED)
.setReason(MediaError.ERROR_REASON_INVALID_REQUEST)
.build()))
} else Tasks.call {
play(convertLoadRequestToMovie(mediaLoadRequestData)!!)
// Update media metadata and state
val mediaManager = castReceiverContext!!.mediaManager
mediaManager.setDataFromLoad(mediaLoadRequestData)
mediaLoadRequestData
}
}
}
private fun convertLoadRequestToMovie(mediaLoadRequestData: MediaLoadRequestData?): Movie? {
if (mediaLoadRequestData == null) {
return null
}
val mediaInfo: MediaInfo = mediaLoadRequestData.getMediaInfo() ?: return null
var videoUrl: String = mediaInfo.getContentId()
if (mediaInfo.getContentUrl() != null) {
videoUrl = mediaInfo.getContentUrl()
}
val metadata: MediaMetadata = mediaInfo.getMetadata()
val movie = Movie()
movie.videoUrl = videoUrl
movie.title = metadata?.getString(MediaMetadata.KEY_TITLE)
movie.description = metadata?.getString(MediaMetadata.KEY_SUBTITLE)
if(metadata?.hasImages() == true) {
movie.cardImageUrl = metadata.images[0].url.toString()
}
return movie
}
Ora che il Callback è stato definito, dobbiamo registrarlo in MediaManager
. Il callback deve essere registrato prima della chiamata a MediaManager.onNewIntent()
. Aggiungi setMediaLoadCommandCallback
quando il player viene inizializzato:
private fun initializePlayer() {
if (mPlayer == null) {
...
mMediaSession = MediaSessionCompat(getContext(), LOG_TAG)
...
castReceiverContext = CastReceiverContext.getInstance()
if (castReceiverContext != null) {
val mediaManager: MediaManager = castReceiverContext.getMediaManager()
mediaManager.setSessionCompatToken(mMediaSession.getSessionToken())
mediaManager.setMediaLoadCommandCallback(MyMediaLoadCommandCallback())
}
}
}
Eseguiamo l'app di esempio
Fai clic sul pulsante Esegui per implementare l'app sul dispositivo ATV. Sul dispositivo di invio, fai clic sul pulsante Trasmetti e seleziona il dispositivo ATV. L'app ATV verrà avviata sul dispositivo ATV. Seleziona un video sul dispositivo mobile e il video inizierà a essere riprodotto su ATV. Controlla se ricevi una notifica sullo smartphone con i controlli di riproduzione. Prova a utilizzare i controlli come pausa; il video sul dispositivo ATV deve essere messo in pausa.
7. Supporto dei comandi di controllo di Google Cast
L'applicazione attuale ora supporta i comandi di base compatibili con una sessione multimediale, come riproduzione, pausa e ricerca. Tuttavia, alcuni comandi di controllo di Google Cast non sono disponibili nella sessione multimediale. Devi registrare un MediaCommandCallback
per supportare questi comandi di controllo di Google Cast.
Aggiungi MyMediaCommandCallback
all'istanza MediaManager
utilizzando setMediaCommandCallback
quando il player viene inizializzato:
private fun initializePlayer() {
...
castReceiverContext = CastReceiverContext.getInstance()
if (castReceiverContext != null) {
val mediaManager = castReceiverContext!!.mediaManager
...
mediaManager.setMediaCommandCallback(MyMediaCommandCallback())
}
}
Crea la classe MyMediaCommandCallback
per eseguire l'override dei metodi, ad esempio onQueueUpdate()
per supportare i comandi di controllo di trasmissione:
private inner class MyMediaCommandCallback : MediaCommandCallback() {
override fun onQueueUpdate(
senderId: String?,
queueUpdateRequestData: QueueUpdateRequestData
): Task<Void> {
Toast.makeText(getActivity(), "onQueueUpdate()", Toast.LENGTH_SHORT).show()
// Queue Prev / Next
if (queueUpdateRequestData.getJump() != null) {
Toast.makeText(
getActivity(),
"onQueueUpdate(): Jump = " + queueUpdateRequestData.getJump(),
Toast.LENGTH_SHORT
).show()
}
return super.onQueueUpdate(senderId, queueUpdateRequestData)
}
}
8. Utilizzo dello stato dei contenuti multimediali
Modifica dello stato dei contenuti multimediali
Cast Connect recupera lo stato di base dei contenuti multimediali dalla sessione multimediale. Per supportare le funzionalità avanzate, la tua app Android TV può specificare e sostituire ulteriori proprietà di stato tramite un MediaStatusModifier
. MediaStatusModifier
funzionerà sempre sul MediaSession
che hai impostato in CastReceiverContext
.
Ad esempio, per specificare setMediaCommandSupported
quando viene attivato il callback onLoad
:
import com.google.android.gms.cast.MediaStatus
...
private class MyMediaLoadCommandCallback : MediaLoadCommandCallback() {
fun onLoad(
senderId: String?,
mediaLoadRequestData: MediaLoadRequestData
): Task<MediaLoadRequestData> {
Toast.makeText(getActivity(), "onLoad()", Toast.LENGTH_SHORT).show()
...
return Tasks.call({
play(convertLoadRequestToMovie(mediaLoadRequestData)!!)
...
// Use MediaStatusModifier to provide additional information for Cast senders.
mediaManager.getMediaStatusModifier()
.setMediaCommandSupported(MediaStatus.COMMAND_QUEUE_NEXT, true)
.setIsPlayingAd(false)
mediaManager.broadcastMediaStatus()
// Return the resolved MediaLoadRequestData to indicate load success.
mediaLoadRequestData
})
}
}
Intercettazione di MediaStatus prima dell'invio
Analogamente a MessageInterceptor
dell'SDK del ricevitore web, puoi specificare un MediaStatusWriter
in MediaManager
per apportare ulteriori modifiche al MediaStatus
prima che venga trasmesso ai mittenti collegati.
Ad esempio, puoi impostare dati personalizzati in MediaStatus
prima di inviare il messaggio a mittenti di dispositivi mobili:
import com.google.android.gms.cast.tv.media.MediaManager.MediaStatusInterceptor
import com.google.android.gms.cast.tv.media.MediaStatusWriter
import org.json.JSONObject
import org.json.JSONException
...
private fun initializePlayer() {
if (mPlayer == null) {
...
if (castReceiverContext != null) {
...
val mediaManager: MediaManager = castReceiverContext.getMediaManager()
...
// Use MediaStatusInterceptor to process the MediaStatus before sending out.
mediaManager.setMediaStatusInterceptor(
MediaStatusInterceptor { mediaStatusWriter: MediaStatusWriter ->
try {
mediaStatusWriter.setCustomData(JSONObject("{myData: 'CustomData'}"))
} catch (e: JSONException) {
Log.e(LOG_TAG,e.message,e);
}
})
}
}
}
9. Complimenti
Ora sai come attivare la trasmissione di un'app per Android TV utilizzando la raccolta Cast Connect.
Per ulteriori dettagli, consulta la guida per gli sviluppatori all'indirizzo /cast/docs/android_tv_receiver.