Un'app per Android compatibile con Google Cast

1. Panoramica

Logo di Google Cast

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 alla TV. Gli utenti potranno quindi utilizzare il proprio dispositivo mobile come telecomando per la riproduzione di contenuti multimediali sulla TV.

L'SDK di Google Cast ti consente di estendere la tua app per controllare una TV o un sistema audio. L'SDK Cast ti consente di aggiungere i componenti necessari dell'interfaccia utente in base all'elenco di controllo di Google Cast Design.

Viene fornito l'elenco di controllo della progettazione di Google Cast, che rende l'esperienza utente semplice e prevedibile su tutte le piattaforme supportate.

Cosa realizzeremo?

Dopo aver completato questo codelab, avrai 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 connettersi a un dispositivo di trasmissione e avviare un ricevitore multimediale.
  • Come trasmettere un video.
  • Come aggiungere un mini controller di trasmissione alla tua app.
  • Come supportare le notifiche di contenuti multimediali e i controlli della schermata di blocco.
  • Come aggiungere un controller espanso.
  • Come fornire un overlay introduttivo.
  • Come personalizzare i widget Google Cast.
  • Come eseguire l'integrazione con Cast Connect

Che cosa ti serve

  • Versione più recente dell'SDK Android.
  • Android Studio versione 3.2 o successive
  • Un dispositivo mobile con Android 4.1+ Jelly Bean (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 Chromecast con Google TV, ma è facoltativo per il resto del codelab. Se non ne hai uno, non esitare a saltare il passaggio Aggiungi assistenza per Cast Connect, verso la fine di questo tutorial.

Esperienza

  • Devi avere una conoscenza precedente di Kotlin e dello sviluppo Android.
  • Devi anche saper saper guardare la TV :)

Come utilizzerai questo tutorial?

Leggilo solo Leggilo e completa gli esercizi

Come valuteresti la tua esperienza con la creazione di app per Android?

Principiante Intermedio Esperto

Come valuteresti la tua esperienza con la TV?

Principiante Intermedio Esperto

2. Recupera il codice campione

Puoi scaricare tutto il codice campione sul computer...

e decomprimi il file ZIP scaricato.

3. Esegui l'app di esempio

icona di una coppia di bussole

Innanzitutto, vediamo come si presenta l'app di esempio completata. L'app è un video player di base. L'utente può selezionare un video da un elenco per poi riprodurlo localmente sul dispositivo o trasmetterlo a un dispositivo Google Cast.

Dopo aver scaricato il codice, segui le istruzioni riportate di seguito per aprire ed eseguire l'app di esempio completata in Android Studio:

Seleziona Importa progetto nella schermata di benvenuto o scegli le opzioni di menu File > Nuovo > Importa progetto....

Seleziona la directory icona cartellaapp-done dalla cartella del codice campione e fai clic su OK.

Fai clic su File > Pulsante "Sincronizza progetto con Gradle" di Android Studio 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 sette volte Numero build. Torna alla schermata precedente, vai a Sistema > Avanzate e tocca Opzioni sviluppatore in basso, quindi tocca Debug USB per attivare la funzionalità.

Collega il tuo dispositivo Android e fai clic sul pulsante Pulsante Esegui di Android Studio, un triangolo verde che punta a destraEsegui in Android Studio. Dopo qualche secondo verrà visualizzata l'app video Trasmetti video.

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.

Un mini controller è ora visibile nella parte inferiore dello schermo. Illustrazione di un telefono Android con l'app "Trasmetti video" con il mini controller visualizzato nella parte inferiore dello schermo

Fai clic sul pulsante di 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. Ora dovresti ricevere una notifica dalla sessione di trasmissione.

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.

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

Illustrazione di un telefono Android con l'app "Trasmetti video"

Dobbiamo aggiungere il supporto per Google Cast all'app di download che hai scaricato. Ecco una terminologia di Google Cast che utilizzeremo in questo codelab:

  • Un'app mittente viene eseguita su un dispositivo mobile o su un laptop.
  • Un'app destinatario viene eseguita sul dispositivo Google Cast.

Ora è tutto pronto per creare il progetto iniziale con Android Studio:

  1. Seleziona la directory icona cartellaapp-start dal download del codice campione (seleziona Importa progetto nella schermata di benvenuto o l'opzione del menu File > Nuovo > Importa progetto...).
  2. Fai clic sul pulsante Pulsante "Sincronizza progetto con Gradle" di Android Studio Sincronizza progetto con file Gradle.
  3. Fai clic sul pulsante Pulsante Esegui di Android Studio, un triangolo verde che punta a destraEsegui 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. Gli utenti possono selezionare un video per visualizzarne i dettagli o riprodurlo localmente sul dispositivo mobile.

L'app è composta da due attività principali: VideoBrowserActivity e LocalPlayerActivity. Per integrare la funzionalità di Google Cast, le attività devono ereditare da AppCompatActivity o dall'elemento principale, FragmentActivity. Questo limite esiste perché dobbiamo aggiungere MediaRouteButton (fornito nella libreria di supporto di MediaRouter) come MediaRouteActionProvider e funziona solo se l'attività eredita dalle classi sopra menzionate. La libreria di supporto di MediaRouter dipende dalla libreria di assistenza AppCompat, che fornisce i corsi richiesti.

Attività VideoBrowser

Questa attività contiene un Fragment (VideoBrowserFragment). L'elenco è supportato da ArrayAdapter (VideoListAdapter). L'elenco dei video e i metadati associati 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). L'oggetto MediaItem viene passato tra le attività, quindi MediaItem ha metodi di utilità per convertirlo in Bundle e viceversa.

Quando il caricatore crea l'elenco MediaItems, lo trasmette al VideoListAdapter, che a sua volta presenta l'elenco MediaItems in VideoBrowserFragment. All'utente viene presentato un elenco di miniature dei video con una breve descrizione per ogni video. Quando un elemento viene selezionato, l'elemento MediaItem corrispondente viene convertito in un elemento Bundle e viene passato a LocalPlayerActivity.

Attività PlayerLocal

Questa attività mostra i metadati di un determinato video e consente all'utente di riprodurlo localmente sul dispositivo mobile.

L'attività ospita un elemento VideoView, alcuni controlli dei contenuti multimediali e un'area di testo per mostrare la descrizione del video selezionato. Il player copre la parte superiore dello schermo, lasciando spazio per la descrizione dettagliata del video. L'utente può riprodurre/mettere in pausa o ricercare la riproduzione locale dei video.

Dipendenze

Poiché utilizziamo AppCompatActivity, abbiamo bisogno della libreria di assistenza di AppCompat. Per gestire l'elenco dei video e ricevere le immagini dell'elenco in modo asincrono, utilizziamo la libreria Volley.

Domande frequenti

5. Aggiunta del pulsante Trasmetti

Illustrazione della parte superiore di un telefono Android con l'app Trasmetti video; il pulsante Trasmetti appare nell'angolo in alto a destra dello schermo

In un'applicazione compatibile con Google Cast, viene visualizzato il pulsante Trasmetti in ogni attività. Fai clic sul pulsante Trasmetti per visualizzare un elenco di dispositivi di trasmissione che un utente può selezionare. Se l'utente stava riproducendo contenuti localmente sul dispositivo di trasmissione, la selezione di un dispositivo di trasmissione avvia o riprende la riproduzione sul dispositivo di trasmissione. Durante una sessione di trasmissione, l'utente può fare clic sul pulsante Trasmetti in qualsiasi momento e interrompere la trasmissione dell'applicazione al dispositivo di trasmissione. L'utente deve essere in grado di connettersi o disconnettersi dal dispositivo di trasmissione durante qualsiasi attività della tua applicazione, come descritto nell'elenco di controllo di Google Cast Design.

Dipendenze

Aggiorna il file build.gradle dell'app per includere 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 le build del progetto senza errori.

Inizializzazione

Il framework di Cast ha un oggetto singleton globale, CastContext, che coordina tutte le interazioni di trasmissione.

Devi implementare l'interfaccia OptionsProvider per fornire CastOptions necessario per inizializzare il singleton CastContext. L'opzione più importante è l'ID applicazione ricevitore, che consente di filtrare i risultati del rilevamento del dispositivo di trasmissione e di avviare l'applicazione di ricezione all'avvio di una sessione di trasmissione.

Quando sviluppi una tua app compatibile con Google Cast, devi registrarti come sviluppatore di dati Cast e ottenere un ID applicazione per la tua app. In 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 il OptionsProvider all'interno del 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 al tag LocalPlayerActivity.

Pulsante Trasmetti

Ora che CastContext è inizializzato, dobbiamo aggiungere il pulsante Trasmetti per consentire all'utente di selezionare un dispositivo di trasmissione. Il pulsante Trasmetti è implementato da MediaRouteButton dalla libreria di supporto MediaRouter. Come per qualsiasi icona delle azioni che puoi aggiungere alle tue attività (utilizzando un 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 che l'elemento delle 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"/>

Sostituisci il 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
}

Sostituisci onCreateOptionsMenu in LocalPlayerActivity in modo simile.

Fai clic sul pulsante Pulsante Esegui di Android Studio, un triangolo verde che punta a destraEsegui per eseguire l'applicazione sul tuo dispositivo mobile. Dovresti trovare un pulsante Trasmetti nella barra delle azioni dell'app e, sulla barra delle app, verranno elencati i dispositivi di trasmissione sulla tua rete locale. Il rilevamento dei dispositivi è gestito automaticamente da CastContext. Seleziona il tuo dispositivo di trasmissione e l'app di ricezione del campione verrà caricata sul dispositivo di trasmissione. Puoi spostarti tra l'attività di navigazione e l'attività locale del player e lo stato del pulsante Trasmetti rimane sincronizzato.

Non abbiamo collegato alcun supporto alla riproduzione di contenuti multimediali, quindi non puoi ancora riprodurre i video sul dispositivo di trasmissione. Fai clic sul pulsante Trasmetti per disconnetterti.

6. Trasmettere contenuti video

Illustrazione di un telefono Android con l&#39;app &quot;Trasmetti video&quot;

Estenderemo l'app di esempio per riprodurre i video anche da remoto su un dispositivo di trasmissione. Per farlo, dobbiamo ascoltare i vari eventi generati dal framework di trasmissione.

Trasmissione di contenuti multimediali

A livello generale, se vuoi riprodurre contenuti multimediali su un dispositivo di trasmissione, procedi nel seguente modo:

  1. Crea un oggetto MediaInfo che modella un elemento multimediale.
  2. Collegati al dispositivo di trasmissione e avvia l'applicazione di ricezione.
  3. Carica l'oggetto MediaInfo nel ricevitore e riproduci il contenuto.
  4. Monitora lo stato dei contenuti multimediali.
  5. Invia comandi di riproduzione al ricevitore in base alle interazioni degli utenti.

Abbiamo già completato il passaggio 2 nella sezione precedente. Il passaggio 3 è facile con il framework di trasmissione. Il passaggio 1 riguarda la mappatura di un oggetto a un altro; MediaInfo è un elemento che il framework Cast comprende e MediaItem è l'incapsulamento della nostra app per un elemento multimediale; possiamo mappare facilmente un MediaItem a un MediaInfo.

L'app di esempio LocalPlayerActivity distingue già tra la riproduzione locale e da remoto utilizzando questo numero:

private var mLocation: PlaybackLocation? = null

enum class PlaybackLocation {
    LOCAL, REMOTE
}

enum class PlaybackState {
    PLAYING, PAUSED, BUFFERING, IDLE
}

Non è importante in questo codelab capire esattamente come funziona tutta la logica del player di esempio. È importante tenere presente che il lettore multimediale dell'app dovrà essere modificato per poter conoscere i due luoghi di visualizzazione in maniera analoga.

Al momento il player locale è sempre in 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 vengono eseguite nel framework di trasmissione. Ad esempio, se iniziamo a trasmettere, dobbiamo interrompere la riproduzione locale e disattivare alcuni controlli. Allo stesso modo, se interrompiamo la trasmissione quando seguiamo questa attività, dobbiamo passare alla riproduzione locale. Per gestire questo aspetto, dobbiamo ascoltare i vari eventi generati dal framework di trasmissione.

Gestione delle sessioni di trasmissione

Per il framework di trasmissione, una sessione di Cast combina i passaggi per la connessione a un dispositivo, l'avvio (o la partecipazione), la connessione a un'applicazione ricevitore e l'inizializzazione di un canale di controllo dei contenuti multimediali, se necessario. Il canale di controllo dei contenuti multimediali è il modo in cui il framework di trasmissione invia e riceve i messaggi dal lettore multimediale del ricevitore.

La sessione di trasmissione viene avviata automaticamente quando l'utente seleziona un dispositivo dal pulsante Trasmetti e si interrompe automaticamente quando l'utente si disconnette. Anche la riconnessione a una sessione del ricevitore a causa di problemi di rete viene gestita automaticamente dall'SDK di trasmissione.

Aggiungiamo un 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, vogliamo essere informati quando siamo connessi o disconnessi dal dispositivo di trasmissione per poter passare al o dal player locale. Tieni presente che la connettività può essere interrotta non solo dall'istanza dell'applicazione in esecuzione sul dispositivo mobile, ma anche da un'altra istanza della tua o di un'altra applicazione in esecuzione su un altro dispositivo mobile.

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 Trasmetti.

Dobbiamo registrare il listener di sessioni e inizializzare alcune variabili che utilizzeremo nell'attività. Modifica il metodo LocalPlayerActivity onCreate 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 di contenuti multimediali in corso...

In Cast SDK, RemoteMediaClient fornisce una serie di API pratiche per gestire la riproduzione di contenuti multimediali remoti sul ricevitore. Per un elemento CastSession che supporta la riproduzione di contenuti multimediali, l'SDK crea automaticamente un'istanza di RemoteMediaClient. Per accedere a questo metodo, chiama 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 diversi metodi esistenti per usare la logica della sessione di trasmissione per supportare la riproduzione da remoto:

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 Pulsante Esegui di Android Studio, un triangolo verde che punta a destraEsegui per eseguire l'applicazione sul 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

Nell'elenco di controllo della progettazione di Cast, tutte le app di trasmissione devono disporre di un mini controller che venga visualizzato quando l'utente esce dalla pagina dei contenuti corrente. Il mini controller offre accesso immediato e un promemoria visibile per la sessione di trasmissione corrente.

Illustrazione di una parte inferiore del telefono Android che mostra il mini player nell&#39;app Video di Google Cast

L'SDK per la trasmissione offre una vista personalizzata, MiniControllerFragment, che può essere aggiunta al file di layout dell'app delle attività in cui vuoi mostrare il mini controller.

Aggiungi la seguente definizione di frammento in fondo sia a res/layout/player_activity.xml sia 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 Pulsante Esegui di Android Studio, un triangolo verde che punta a destraEsegui per eseguire l'app e trasmettere un video. Quando la riproduzione inizia sul ricevitore, dovresti vedere il mini controller in fondo a ogni attività. Puoi controllare la riproduzione da remoto utilizzando il mini controller. Se passi dall'attività di esplorazione all'attività del player locale, 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 un'app mittente per implementare i controlli multimediali da una notifica e la schermata di blocco.

Illustrazione di un telefono Android che mostra i controlli multimediali nell&#39;area delle notifiche

L'SDK Cast fornisce MediaNotificationService per aiutare l'app mittente a creare controlli multimediali per la notifica e la schermata di blocco. Il servizio viene unito automaticamente al manifest della tua app in base al grado.

L'elemento MediaNotificationService verrà usato in background durante la trasmissione del mittente e mostrerà una notifica con una miniatura dell'immagine e i 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 CastOptions durante l'inizializzazione di CastContext. I controlli multimediali per la notifica e la schermata di blocco sono attivi per impostazione predefinita. La funzionalità Schermata di blocco viene attivata soltanto se vengono attivate le notifiche.

Modifica CastOptionsProvider e cambia l'implementazione di 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 Pulsante Esegui di Android Studio, un triangolo verde che punta a destraEsegui per eseguire l'applicazione sul tuo dispositivo mobile. Trasmetti un video e chiudi l'app di esempio. Dovresti ricevere una notifica relativa al video attualmente in riproduzione sul ricevitore. Ora, blocca il dispositivo mobile e la schermata di blocco dovrebbe mostrare i controlli per la riproduzione di contenuti multimediali sul dispositivo di trasmissione.

Illustrazione di un telefono Android che mostra i controlli multimediali nella schermata di blocco

9. Overlay introduttivo

L'elenco di controllo della progettazione di Google Cast richiede a un'app mittente di presentare il pulsante Trasmetti agli utenti esistenti per informarli che l'app relativa ai mittenti ora supporta la trasmissione e aiuta anche gli utenti che non hanno familiarità con Google Cast.

Illustrazione che mostra l&#39;overlay introduttivo di Google Cast sul pulsante Trasmetti nell&#39;app Android per la trasmissione di video

L'SDK Cast offre una vista personalizzata, IntroductoryOverlay, che può essere utilizzata per evidenziare il pulsante Trasmetti quando viene mostrato per la prima volta agli utenti. Aggiungi il codice seguente 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()
        }
    }
}

Aggiungi un metodo CastStateListener e chiama il metodo showIntroductoryOverlay quando un dispositivo di trasmissione è disponibile modificando il metodo onCreate e sostituendo i metodi onResume e onPause in modo che corrispondano a quanto segue:

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 rimuovila dal tuo dispositivo. Successivamente, fai clic sul pulsante Pulsante Esegui di Android Studio, un triangolo verde che punta a destraEsegui per eseguire l'app sul tuo dispositivo mobile e 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 a un'app mittente di fornire un controller espanso per i contenuti multimediali trasmessi. Il controller espanso è una versione a schermo intero del mini controller.

Illustrazione di un video in riproduzione su un telefono Android con il controller espanso in overlay

L'SDK Cast fornisce un widget per il controller espanso denominato ExpandedControllerActivity. Si tratta di una classe astratta che è necessaria per aggiungere un pulsante Trasmetti.

Innanzitutto, crea un nuovo file di risorse di 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. Quindi, 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 il ExpandedControlsActivity nel AndroidManifest.xml all'interno del tag application sopra il 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 modifica 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 LocalPlayerActivity loadRemoteMedia per visualizzare ExpandedControlsActivity quando viene caricato il supporto 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 Pulsante Esegui di Android Studio, un triangolo verde che punta a destraEsegui per eseguire l'app sul tuo dispositivo mobile e trasmettere un video. Dovresti visualizzare il controller espanso. Torna all'elenco dei video e quando fai clic sul mini controller, quest'ultimo verrà caricato di nuovo. Esci dall'app per visualizzare la notifica. Fai clic sull'immagine di notifica per caricare il controller espanso.

11. Aggiungi il supporto di Cast Connect

La raccolta Cast Connect consente alle applicazioni esistenti del mittente di comunicare con le applicazioni Android TV tramite il protocollo Cast. Cast Connect si basa sull'infrastruttura di trasmissione e l'app Android TV funge da ricevitore.

Dipendenze

Nota: per implementare Cast Connect, il play-services-cast-framework deve essere 19.0.0 o superiore.

Opzioni di lancio

Per poter lanciare l'applicazione Android TV, nota anche come Ricevitore Android, è necessario impostare il flag setAndroidReceiverCompatible su true nell'oggetto LaunchOptions. Questo oggetto LaunchOptions determina il modo in cui il ricevitore viene lanciato e viene passato all'oggetto CastOptions restituito dalla classe CastOptionsProvider. Se imposti il flag indicato sopra 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 lancio

Sul lato del mittente, puoi specificare CredentialsData per indicare chi partecipa alla sessione. Il credentials è una stringa che può essere definita dall'utente, purché l'app ATV possa comprenderlo. Il CredentialsData verrà trasmesso alla tua app Android TV soltanto al momento del lancio o durante la registrazione. Se lo imposti di nuovo mentre è connesso, non verrà trasmesso all'app Android TV.

Per impostare le credenziali di avvio, è necessario definire CredentialsData e passare all'oggetto LaunchOptions. Aggiungi il codice seguente 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

Se l'app Ricevitore web e l'app Android TV gestiscono credentials in modo diverso, potrebbe essere necessario definire credentials distinte per ciascun dispositivo. A questo scopo, aggiungi il seguente codice al 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 viene trasmesso il mittente, l'SDK gestisce automaticamente le credenziali da utilizzare per la sessione corrente.

Test di Cast Connect

Procedura per installare l'APK Android TV su Chromecast con Google TV

  1. Cerca l'indirizzo IP del tuo dispositivo Android TV. Solitamente disponibile in Impostazioni > Rete e Internet > (Nome della rete a cui è connesso il dispositivo). Sulla destra vengono visualizzati i dettagli e l'IP del dispositivo sulla rete.
  2. Utilizza l'indirizzo IP del dispositivo per connetterti tramite ADB tramite il terminale:
$ adb connect <device_ip_address>:5555
  1. Dalla finestra del terminale, passa alla cartella di primo livello per visualizzare gli esempi di codelab che hai scaricato all'inizio di questo codelab. Ad esempio:
$ cd Desktop/android_codelab_src
  1. Installa il file .apk in questa cartella sull'Android TV eseguendo:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Ora dovresti essere in grado di vedere un'app denominata Trasmetti video nel menu Le tue app del tuo dispositivo Android TV.
  2. 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 di trasmissione e seleziona il tuo dispositivo Android TV tra le opzioni disponibili. A questo punto, dovresti vedere l'app Android TV avviata sul tuo dispositivo Android TV e riprodurre un video dovrebbe consentirti di controllare la riproduzione del video usando il telecomando di Android TV.

12. Personalizzare i widget Cast

Puoi personalizzare i widget di trasmissione impostando i colori, impostando lo stile dei pulsanti, del testo e dell'aspetto delle miniature, nonché 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 un'app video di Google Cast utilizzando i widget dell'SDK di trasmissione su Android.

Per maggiori dettagli, consulta la guida per gli sviluppatori Android Sender.