1. Panoramica
Questo codelab ti insegnerà come modificare un'app esistente di Android TV per supportare la trasmissione e la comunicazione di app Cast Cast esistenti.
Che cos'è Google Cast e Google Cast Connect?
Google Cast consente agli utenti di trasmettere contenuti da un dispositivo mobile alla TV. In genere una sessione di Google Cast è costituita da due componenti: un mittente e un'applicazione destinatario. Le applicazioni del mittente, come un'app o un sito web per dispositivi mobili come youtube.com, avviano e controllano la riproduzione di un'applicazione di ricezione di trasmissioni. Le applicazioni di ricezione di trasmissioni sono app HTML 5 supportate su Chromecast e sui dispositivi Android TV.
Quasi tutto lo stato in una sessione di trasmissione viene archiviato nell'applicazione del ricevitore. Quando lo stato viene aggiornato, ad esempio se viene caricato un nuovo elemento multimediale, viene trasmesso uno stato dei contenuti multimediali a tutti i mittenti. Queste trasmissioni contengono lo stato corrente della sessione di trasmissione. Le applicazioni del mittente utilizzano questo stato dei contenuti multimediali per visualizzare informazioni di riproduzione nella loro UI.
Cast Connect si basa su questa infrastruttura e l'app per Android TV funge da ricevitore. La raccolta di Cast Connect consente alla tua app Android TV di ricevere messaggi e trasmettere lo stato dei contenuti multimediali come se fosse un'applicazione di ricezione di trasmissioni.
Cosa realizzeremo?
Una volta completato il codelab, potrai utilizzare le app di trasmissione dei Cast per trasmettere video a un'app Android TV. L'app Android TV può comunicare anche con le app dei mittenti tramite il protocollo Cast.
Obiettivi didattici
- Come aggiungere la raccolta di Connect Connect a un'app ATV di esempio.
- Come collegare un mittente di Google Cast e avviare l'app ATV.
- Come avviare la riproduzione di contenuti multimediali sull'app ATV da un'app di trasmissione dei contenuti.
- Come inviare lo stato dei contenuti multimediali dall'app ATV alle app di trasmissione dei mittenti.
Che cosa ti serve
- L'ultimo SDK Android.
- Versione più recente di Android Studio. Nello specifico,
Chipmunk | 2021.2.1
o versioni successive. - Un dispositivo Android TV che ha attivato le opzioni per sviluppatori e il debug USB.
- Un telefono Android che abbia attivato le opzioni per sviluppatori e il debug USB.
- Un cavo dati USB per collegare il telefono 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...
e decomprimi il file ZIP scaricato.
3. Esegui l'app di esempio
Innanzitutto, vediamo come si presenta l'app di esempio completata. L'app Android TV utilizza l'UI di Coldline e un video player di base. L'utente può selezionare da un elenco un video che verrà riprodotto sulla TV una volta selezionato. Con l'app del mittente su dispositivo mobile, l'utente può trasmettere un video anche all'app Android TV.
Registra dispositivi sviluppatore
Per attivare le funzionalità di trasmissione di Connect per lo sviluppo di applicazioni, devi registrare il numero di serie del dispositivo Chromecast integrato con il dispositivo Android TV che utilizzerai nella Console per gli sviluppatori di Google Cast. Puoi trovare il numero di serie nella sezione 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 con il metodo descritto sopra.
Senza registrazione, Cast Connect funzionerà soltanto per app installate dal Google Play Store per motivi di sicurezza. Dopo 15 minuti dall'inizio della procedura di registrazione, riavvia il dispositivo.
Installare l'app mittente Android
Per eseguire il test delle richieste di invio da un dispositivo mobile, abbiamo fornito una semplice applicazione per i mittenti chiamata Cast Videos come file mobile-sender-0629.apk
nel file ZIP del codice sorgente. Stiamo per usare ADB per installare l'APK. Se hai già installato una versione diversa di Trasmetti video, disinstalla tale versione da tutti i profili sul dispositivo prima di continuare.
- Attiva le opzioni sviluppatore e il debug USB sul tuo telefono Android.
- Collega un cavo dati USB per collegare il telefono Android al computer di sviluppo.
- Installa
mobile-sender-0629.apk
sul tuo telefono Android.
- Puoi trovare l'app del mittente Trasmetti video sul tuo telefono Android.
Installa l'app Android TV
Le seguenti istruzioni descrivono come aprire ed eseguire l'app di esempio completata in Android Studio:
- Seleziona il comando 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 le opzioni sviluppatore e il debug USB sul tuo dispositivo Android TV.
- ADB si connette al tuo dispositivo Android TV, che dovrebbe essere visualizzato in Android Studio.
- Fai clic sul pulsante
Esegui. Dovresti vedere l'app ATV denominata Cast Connect Codelab, visualizzata dopo alcuni secondi.
Giochiamo a Cast Connect con l'app ATV
- Vai alla schermata Home di Android TV.
- Apri l'app Cast Video Mittente dal telefono Android. Fai clic sul pulsante Trasmetti
e seleziona il tuo dispositivo ATV.
- L'app Cast Connect Codelab ATV verrà avviata sulla tua ATV e il pulsante Trasmetti nel mittente indicherà che è connesso
.
- Seleziona un video dall'app ATV per avviare la riproduzione del video sulla tua ATV.
- Sul telefono cellulare, ora è disponibile un mini controller nella parte inferiore dell'app del mittente. Puoi usare il pulsante di riproduzione/pausa per controllare la riproduzione.
- Seleziona un video dal cellulare e riproducilo. La riproduzione del video inizierà sulla tua ATV e sul controller mobile verrà visualizzato il controller espanso.
- Blocca il telefono e quando lo sblocchi, dovresti vedere una notifica sulla schermata di blocco per controllare la riproduzione dei contenuti multimediali o interrompere la trasmissione.
4. Prepara il progetto di avvio
Ora che abbiamo verificato l'integrazione dell'app Cast Connect completata, dobbiamo aggiungere il supporto per Cast Connect all'app iniziale che hai scaricato. Ora puoi iniziare a sviluppare il progetto iniziale utilizzando Android Studio:
- Seleziona il comando 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 progetto con file Gradle.
- Seleziona il dispositivo ATV e fai clic sul pulsante
Run (Esegui) per eseguire l'app ed esplorare l'interfaccia utente.
Progettazione di app
L'app fornisce un elenco di video per l'utente. Gli utenti possono selezionare un video da guardare 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 i relativi metadati associati sono configurati nella classe MovieList
e il metodo setupMovies()
viene chiamato per creare un elenco di Movie
oggetti.
Un oggetto Movie
rappresenta un'entità video con titolo, descrizione, Non mi piace e URL del video. Ogni oggetto Movie
è associato a un elemento CardPresenter
per presentare la miniatura del video con il titolo e lo studio e viene trasmesso a ArrayObjectAdapter
.
Quando viene selezionato un elemento, l'oggetto Movie
corrispondente viene passato a PlaybackActivity
.
Attività di riproduzione
Questa attività contiene un frammento (PlaybackVideoFragment
) che ospita un VideoView
con ExoPlayer
, alcuni controlli multimediali e un'area di testo per mostrare la descrizione del video selezionato e consente all'utente di guardare il video su Android TV. L'utente può utilizzare il telecomando per riprodurre/mettere in pausa o ricercare 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 Android TV, devi creare e supportare eventi da una sessione multimediale. La raccolta di Cast Connect genera lo stato dei contenuti multimediali in base allo stato della sessione multimediale. La tua sessione multimediale viene usata anche dalla raccolta di Cast Connect per segnalare quando ha ricevuto determinati messaggi da un mittente, ad esempio la pausa.
5. Configurazione dell'assistenza per la trasmissione
Dipendenze
Aggiorna il file build.gradle
dell'app per includere le dipendenze di libreria 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 confermare le build del progetto 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 CastReceiverOptionsProvider.kt
file 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 ricezione 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 mittente della trasmissione, seleziona un'attività che vuoi avviare. In questo codelab, avvieremo la MainActivity
dell'app all'avvio di una sessione di trasmissione. Nel file AndroidManifest.xml
, aggiungi il filtro per intent di lancio 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 di ricezione Cast
Dovresti avviare CastReceiverContext
all'avvio dell'app e interrompere CastReceiverContext
quando l'app viene spostata in background. Ti consigliamo di utilizzare il comando LifecycleObserver
della libreria androidx.life 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()
, CastReceiverContext
in caso di ripristino dell'applicazione e stop()
quando l'applicazione è 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à del singleton CastReceiverContext
, gestisce lo stato dei contenuti multimediali, gestisce l'intent di caricamento, traduce i messaggi dello spazio dei nomi dei media dai mittenti in comandi multimediali e invia lo stato dei contenuti multimediali ai mittenti.
Quando crei un MediaSession
, devi fornire anche il token del MediaSession
attuale 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 il valore di MediaSession
sia 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 riproduzione inattiva, devi impostare un token null su MediaManager
:
private fun releasePlayer() {
mMediaSession?.release()
castReceiverContext?.mediaManager?.setSessionCompatToken(null)
...
}
Eseguiamo l'app di esempio
Fai clic sul pulsante Esegui per eseguire il deployment dell'app sul tuo dispositivo ATV, chiudere l'app e tornare alla schermata Home di ATV. Dal mittente, fai clic sul pulsante Trasmetti
e seleziona il tuo dispositivo ATV. Vedrai che l'app ATV è stata avviata sul dispositivo ATV e lo stato del pulsante Trasmetti è connesso.
6. Caricamento dei contenuti multimediali
Il comando di caricamento viene inviato tramite un intent con il nome del pacchetto definito nella console per gli sviluppatori. Devi aggiungere il seguente filtro per intent predefinito nell'app 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 carico su Android TV
Ora che l'attività è configurata per ricevere questo intent contenente una richiesta di carico, dovremo gestirla.
Quando inizia l'attività, l'app chiama un metodo privato chiamato processIntent
. Questo metodo contiene la logica per l'elaborazione degli intent in arrivo. Per gestire una richiesta di carico, 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.
...
}
In seguito, 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
, quindi, viene aggiornato con MediaLoadRequest
e trasmette il MediaStatus
ai mittenti connessi. Crea una classe privata nidificata chiamata 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 la funzione di callback è stata definita, dobbiamo registrarla su MediaManager
. Il callback deve essere registrato prima della chiamata al numero MediaManager.onNewIntent()
. Aggiungi setMediaLoadCommandCallback
quando il player è 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 eseguire il deployment dell'app sul tuo dispositivo ATV. Dal mittente, fai clic sul pulsante Trasmetti
e seleziona il tuo dispositivo ATV. L'app ATV verrà avviata sul dispositivo ATV. Seleziona un video su dispositivo mobile, la riproduzione del video inizierà sull'ATV. Controlla se hai ricevuto una notifica sul telefono in cui sono disponibili dei controlli di riproduzione. Prova a utilizzare i controlli, ad esempio la pausa, per mettere in pausa il video sul dispositivo ATV.
7. Supportare i comandi di controllo della trasmissione
L'applicazione corrente ora supporta comandi di base compatibili con una sessione multimediale, ad esempio riproduzione, pausa e ricerca. Tuttavia, alcuni comandi di controllo della trasmissione non sono disponibili nella sessione multimediale. Devi registrare un MediaCommandCallback
per supportare i comandi di controllo della trasmissione.
Aggiungi MyMediaCommandCallback
all'istanza MediaManager
utilizzando setMediaCommandCallback
quando il player è inizializzato:
private fun initializePlayer() {
...
castReceiverContext = CastReceiverContext.getInstance()
if (castReceiverContext != null) {
val mediaManager = castReceiverContext!!.mediaManager
...
mediaManager.setMediaCommandCallback(MyMediaCommandCallback())
}
}
Crea una classe MyMediaCommandCallback
per eseguire l'override dei metodi, ad esempio onQueueUpdate()
per supportare i comandi di controllo della 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 dei contenuti multimediali di base dalla sessione multimediale. Per supportare le funzionalità avanzate, l'app Android TV può specificare e sostituire altre proprietà di stato tramite MediaStatusModifier
. MediaStatusModifier
funzionerà sempre sulla 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 dello stato dei contenuti multimediali prima dell'invio
Analogamente all'MessageInterceptor
dell'SDK del ricevitore web, puoi specificare un MediaStatusWriter
nell'MediaManager
per eseguire ulteriori modifiche al tuo MediaStatus
prima che venga trasmesso ai mittenti connessi.
Ad esempio, puoi impostare dati personalizzati in MediaStatus
prima di inviare messaggi ai mittenti mobile:
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. Congratulazioni
Ora sai come trasmettere un'app Android TV con la libreria Cast Connect.
Per ulteriori dettagli, consulta la guida per gli sviluppatori: /cast/docs/android_tv_recipientr.