Un'app Android compatibile con Google Cast

1. Panoramica

Logo di Google Cast

Questo codelab ti insegnerà a modificare un'app video Android esistente per trasmettere contenuti su un dispositivo compatibile con Google Cast.

Che cos'è Google Cast?

Google Cast consente agli utenti di trasmettere contenuti da un dispositivo mobile a una TV. Gli utenti possono quindi utilizzare il proprio dispositivo mobile come telecomando per la riproduzione di contenuti multimediali sulla TV.

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

L'elenco di controllo per la progettazione di Google Cast è fornito per rendere l'esperienza utente di Cast semplice e prevedibile su tutte le piattaforme supportate.

Cosa realizzeremo?

Al termine di 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 controller Cast Mini alla tua app.
  • Come supportare le notifiche multimediali e i controlli della schermata di blocco.
  • Come aggiungere un controller espanso.
  • Come fornire un overlay introduttivo.
  • Come personalizzare i widget di trasmissione.
  • Come eseguire l'integrazione con Cast Connect

Che cosa ti serve

  • L'SDK Android più recente.
  • Android Studio versione 3.2 o successive
  • Un dispositivo mobile con Android 4.1 Jelly Bean e versioni successive (livello API 16).
  • Un cavo dati USB per collegare il dispositivo mobile al computer di sviluppo.
  • Un dispositivo Google Cast, ad esempio Chromecast o Android TV, configurato con accesso a internet.
  • Una TV o un monitor con ingresso HDMI.
  • Per testare l'integrazione di Cast Connect è necessario Chromecast con Google TV, ma è facoltativo per il resto del codelab. Se non ne hai uno, puoi saltare il passaggio Aggiungere il supporto di Cast Connect verso la fine di questo tutorial.

Esperienza

  • Devi avere conoscenze pregresse di sviluppo in Kotlin e Android.
  • Inoltre, dovrai avere già esperienza di visione della TV.

Come utilizzerai questo tutorial?

Solo lettura Leggi e completa gli esercizi

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

Principiante Intermedio Proficiente

Come valuteresti la tua esperienza di visione della TV?

Principiante Intermedio Proficiente

2. recupera il codice campione

Puoi scaricare tutto il codice di esempio sul tuo computer...

ed espandi il file ZIP scaricato.

3. Esegui l'app di esempio

icona di un paio di compassi

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

Una volta scaricato il codice, le seguenti istruzioni descrivono come aprire ed eseguire l'app di esempio completata in Android Studio:

Nella schermata di benvenuto, seleziona Importa progetto o le opzioni del menu File > Nuovo > Importa progetto....

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

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

Collega il dispositivo Android e fai clic sul pulsante Pulsante Esegui di Android Studio, un triangolo verde rivolto verso destraEsegui in Android Studio. Dopo alcuni secondi dovresti vedere 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.

Il video inizierà a essere riprodotto sul tuo dispositivo Google Cast.

Viene visualizzato il controller espanso. Puoi usare il pulsante Riproduci/Metti in pausa per controllare la riproduzione.

Torna all'elenco dei video.

Ora nella parte inferiore dello schermo è visibile un mini controller. Illustrazione di uno smartphone Android su cui è in esecuzione l'app "Trasmetti video" con il mini controller visualizzato nella parte inferiore dello schermo

Fai clic sul pulsante Pausa del mini controller per mettere in pausa il video sul ricevitore. Fai clic sul pulsante di riproduzione nel mini controller per continuare a riprodurre il video.

Fai clic sul pulsante Home del dispositivo mobile. Trascina le notifiche verso il basso. Dovresti vedere una notifica relativa alla sessione di trasmissione.

Blocca lo smartphone e, quando lo sblocchi, dovresti vedere una notifica nella schermata di blocco per controllare la riproduzione dei contenuti multimediali o interrompere la trasmissione.

Torna all'app video e fai clic sul pulsante Trasmetti per interrompere la trasmissione sul dispositivo Google Cast.

Domande frequenti

4. Prepara il progetto iniziale

Illustrazione di uno smartphone Android su cui è in esecuzione l'app "Trasmetti video"

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

  • un'app di mittente in esecuzione su un dispositivo mobile o un laptop,
  • sul dispositivo Google Cast viene eseguita un'app di ricevitore.

Ora puoi iniziare a sviluppare il progetto iniziale utilizzando Android Studio:

  1. Seleziona la directory icona cartellaapp-start dal download del codice di esempio (seleziona Importa progetto nella schermata di benvenuto o l'opzione di menu File > Nuovo > Importa progetto...).
  2. Fai clic sul pulsante Pulsante "Sincronizza progetto con Gradle" di Android Studio Sincronizza progetto con i 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 un elenco che l'utente può sfogliare. Gli utenti possono selezionare un video per visualizzarne i dettagli o riprodurlo localmente sui dispositivi mobili.

L'app è composta da due attività principali: VideoBrowserActivity e LocalPlayerActivity. Per integrare la funzionalità Google Cast, le Attività devono ereditare da AppCompatActivity o dal relativo elemento principale FragmentActivity. Questa limitazione esiste perché dovremmo aggiungere MediaRouteButton (fornito nella libreria di supporto MediaRouter) come MediaRouteActionProvider e questo funzionerà solo se l'attività eredita dai classi sopra menzionati. La libreria di supporto MediaRouter dipende dalla libreria di supporto AppCompat che fornisce le classi richieste.

VideoBrowserActivity

Questa attività contiene un elemento Fragment (VideoBrowserFragment). Questo elenco è supportato da un elemento ArrayAdapter (VideoListAdapter). L'elenco dei video e i relativi metadati sono ospitati su un server remoto sotto forma di file JSON. Un AsyncTaskLoader (VideoItemLoader) recupera questo JSON e lo elabora per creare un elenco di oggetti MediaItem.

Un oggetto MediaItem modella un video e i relativi metadati associati, come titolo, descrizione, URL dello stream, URL delle immagini di supporto ed eventuali tracce di testo associate (per i sottotitoli codificati). L'oggetto MediaItem viene passato da un'attività all'altra, pertanto MediaItem dispone di metodi di utilità per convertirlo in un Bundle e viceversa.

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

LocalPlayerActivity

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

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

Dipendenze

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

Domande frequenti

5. Aggiungere il pulsante Trasmetti

Illustrazione della parte superiore di uno smartphone Android con l'app Trasmetti video in esecuzione; il pulsante Trasmetti viene visualizzato nell'angolo in alto a destra dello schermo

Un'applicazione compatibile con Google Cast mostra il pulsante Trasmetti in ciascuna delle sue attività. Se fai clic sul pulsante Trasmetti, viene visualizzato un elenco di dispositivi di trasmissione che un utente può selezionare. Se l'utente stava riproducendo contenuti localmente sul dispositivo di invio, la selezione di un dispositivo di trasmissione avvia o riprende la riproduzione su quel dispositivo. In qualsiasi momento durante una sessione di trasmissione, l'utente può fare clic sul pulsante Trasmetti e interrompere la trasmissione dell'applicazione sul 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 per la progettazione di Google Cast.

Dipendenze

Aggiorna il file build.gradle dell'app in modo da 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 verificare che le build vengano eseguite senza errori.

Inizializzazione

Il framework 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, utilizzato per filtrare i risultati di rilevamento dei dispositivi di trasmissione e per avviare l'applicazione del ricevitore all'avvio di una sessione di trasmissione.

Quando sviluppi la tua app compatibile con Google Cast, devi registrarti come sviluppatore Google Cast e ottenere un ID applicazione per la tua app. Per questo codelab utilizzeremo un ID app di esempio.

Aggiungi il seguente nuovo file CastOptionsProvider.kt al pacchetto com.google.sample.cast.refplayer del progetto:

package com.google.sample.cast.refplayer

import android.content.Context
import com.google.android.gms.cast.framework.OptionsProvider
import com.google.android.gms.cast.framework.CastOptions
import com.google.android.gms.cast.framework.SessionProvider

class CastOptionsProvider : OptionsProvider {
    override fun getCastOptions(context: Context): CastOptions {
        return CastOptions.Builder()
                .setReceiverApplicationId(context.getString(R.string.app_id))
                .build()
    }

    override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
        return null
    }
}

Ora dichiara OptionsProvider 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 in modo lazy CastContext nel metodo onCreate di VideoBrowserActivity:

import com.google.android.gms.cast.framework.CastContext

private var mCastContext: CastContext? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.video_browser)
    setupActionBar()

    mCastContext = CastContext.getSharedInstance(this)
}

Aggiungi la stessa logica di inizializzazione a LocalPlayerActivity.

Pulsante Trasmetti

Ora che l'CastContext è stato inizializzato, dobbiamo aggiungere il pulsante Trasmetti per consentire all'utente di selezionare un dispositivo di trasmissione. Il pulsante Trasmetti è implementato dal MediaRouteButton della libreria di assistenza di MediaRouter. Come per qualsiasi icona di azione che puoi aggiungere alla tua attività (utilizzando ActionBar o Toolbar), devi prima aggiungere il relativo elemento di menu al menu.

Modifica il file res/menu/browse.xml e aggiungi l'elemento MediaRouteActionProvider nel menu prima dell'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 Cast:

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 rivolto verso destraEsegui per eseguire l'app sul tuo dispositivo mobile. Nella barra delle azioni dell'app dovresti vedere un pulsante Trasmetti. Se fai clic sul pulsante, vengono elencati i dispositivi Google Cast sulla tua rete locale. Il rilevamento dei dispositivi è gestito automaticamente da CastContext. Seleziona il tuo dispositivo di trasmissione e l'app ricevitore di esempio verrà caricata sul dispositivo di trasmissione. Puoi passare dall'attività di navigazione all'attività del player locale e lo stato del pulsante Trasmetti viene mantenuto sincronizzato.

Non abbiamo attivato il supporto per la riproduzione di contenuti multimediali, quindi non puoi ancora riprodurre video sul dispositivo di trasmissione. Fai clic sul pulsante Trasmetti per disconnetterti.

6. Trasmissione di contenuti video

Illustrazione di uno smartphone Android su cui è in esecuzione l&#39;app &quot;Trasmetti video&quot;

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

Trasmissione di contenuti multimediali

In linea generale, se vuoi riprodurre contenuti multimediali su un dispositivo di trasmissione, devi:

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

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

L'app di esempio LocalPlayerActivity distingue già la riproduzione locale da quella remota utilizzando questo enum:

private var mLocation: PlaybackLocation? = null

enum class PlaybackLocation {
    LOCAL, REMOTE
}

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

In questo codelab non è importante che tu capisca esattamente come funziona tutta la logica del player di esempio. È importante capire che il media player dell'app dovrà essere modificato in modo da rilevare le due posizioni di riproduzione in modo simile.

Al momento, il player locale è sempre nello stato di riproduzione locale perché non sa ancora nulla degli stati di trasmissione. Dobbiamo aggiornare l'interfaccia utente in base alle transizioni di stato che si verificano nel framework di trasmissione. Ad esempio, se avviamo la trasmissione, dobbiamo interrompere la riproduzione locale e disattivare alcuni controlli. Analogamente, se interrompiamo la trasmissione quando siamo in questa attività, dobbiamo passare alla riproduzione locale. Per gestire questo problema, dobbiamo ascoltare i vari eventi generati dal framework Cast.

Gestione delle sessioni di trasmissione

Per il framework Cast, una sessione di trasmissione combina i passaggi di connessione a un dispositivo, di lancio (o partecipazione), di connessione a un'applicazione di ricezione e di inizializzazione di un canale di controllo dei contenuti multimediali, se opportuno. Il canale di controllo dei contenuti multimediali è il modo in cui il framework Cast invia e riceve messaggi dal media player del ricevitore.

La sessione di trasmissione verrà avviata automaticamente quando l'utente seleziona un dispositivo dal pulsante Trasmetti e verrà interrotta automaticamente quando l'utente si disconnette. L'SDK Cast gestisce automaticamente anche la riconnessione alla sessione del ricevitore a causa di problemi di rete.

Aggiungiamo SessionManagerListener a LocalPlayerActivity:

import com.google.android.gms.cast.framework.CastSession
import com.google.android.gms.cast.framework.SessionManagerListener
...

private var mSessionManagerListener: SessionManagerListener<CastSession>? = null
private var mCastSession: CastSession? = null
...

private fun setupCastListener() {
    mSessionManagerListener = object : SessionManagerListener<CastSession> {
        override fun onSessionEnded(session: CastSession, error: Int) {
            onApplicationDisconnected()
        }

        override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) {
            onApplicationConnected(session)
        }

        override fun onSessionResumeFailed(session: CastSession, error: Int) {
            onApplicationDisconnected()
        }

        override fun onSessionStarted(session: CastSession, sessionId: String) {
            onApplicationConnected(session)
        }

        override fun onSessionStartFailed(session: CastSession, error: Int) {
            onApplicationDisconnected()
        }

        override fun onSessionStarting(session: CastSession) {}
        override fun onSessionEnding(session: CastSession) {}
        override fun onSessionResuming(session: CastSession, sessionId: String) {}
        override fun onSessionSuspended(session: CastSession, reason: Int) {}
        private fun onApplicationConnected(castSession: CastSession) {
            mCastSession = castSession
            if (null != mSelectedMedia) {
                if (mPlaybackState == PlaybackState.PLAYING) {
                    mVideoView!!.pause()
                    loadRemoteMedia(mSeekbar!!.progress, true)
                    return
                } else {
                    mPlaybackState = PlaybackState.IDLE
                    updatePlaybackLocation(PlaybackLocation.REMOTE)
                }
            }
            updatePlayButton(mPlaybackState)
            invalidateOptionsMenu()
        }

        private fun onApplicationDisconnected() {
            updatePlaybackLocation(PlaybackLocation.LOCAL)
            mPlaybackState = PlaybackState.IDLE
            mLocation = PlaybackLocation.LOCAL
            updatePlayButton(mPlaybackState)
            invalidateOptionsMenu()
       }
   }
}

Nell'attività LocalPlayerActivity, ci interessa essere informati quando ci connettiamo o disconnetti dal dispositivo di trasmissione in modo da poter passare al player locale o viceversa. Tieni presente che la connettività può essere interrotta non solo dall'istanza della tua 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 rimosse automaticamente in risposta alle interazioni dell'utente con le finestre di dialogo di trasmissione.

Dobbiamo registrare l'ascoltatore di sessione 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 dei contenuti multimediali

Nell'SDK Cast, RemoteMediaClient fornisce un insieme di comode API per gestire la riproduzione di contenuti multimediali remoti sul ricevitore. Per un CastSession che supporta la riproduzione di contenuti multimediali, l'SDK creerà automaticamente un'istanza di RemoteMediaClient. Puoi accedervi chiamando il metodo getRemoteMediaClient() sull'istanza CastSession. Aggiungi i seguenti metodi a LocalPlayerActivity per caricare il video attualmente selezionato sul ricevitore:

import com.google.android.gms.cast.framework.media.RemoteMediaClient
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaLoadOptions
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.common.images.WebImage
import com.google.android.gms.cast.MediaLoadRequestData

private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
    if (mCastSession == null) {
        return
    }
    val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return
    remoteMediaClient.load( MediaLoadRequestData.Builder()
                .setMediaInfo(buildMediaInfo())
                .setAutoplay(autoPlay)
                .setCurrentTime(position.toLong()).build())
}

private fun buildMediaInfo(): MediaInfo? {
    val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
    mSelectedMedia?.studio?.let { movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, it) }
    mSelectedMedia?.title?.let { movieMetadata.putString(MediaMetadata.KEY_TITLE, it) }
    movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(0))))
    movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia!!.getImage(1))))
    return mSelectedMedia!!.url?.let {
        MediaInfo.Builder(it)
            .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
            .setContentType("videos/mp4")
            .setMetadata(movieMetadata)
            .setStreamDuration((mSelectedMedia!!.duration * 1000).toLong())
            .build()
    }
}

Ora aggiorna i vari metodi esistenti per utilizzare la logica della sessione di trasmissione per supportare la riproduzione remota:

private fun play(position: Int) {
    startControllersTimer()
    when (mLocation) {
        PlaybackLocation.LOCAL -> {
            mVideoView!!.seekTo(position)
            mVideoView!!.start()
        }
        PlaybackLocation.REMOTE -> {
            mPlaybackState = PlaybackState.BUFFERING
            updatePlayButton(mPlaybackState)
            //seek to a new position within the current media item's new position 
            //which is in milliseconds from the beginning of the stream
            mCastSession!!.remoteMediaClient?.seek(position.toLong())
        }
        else -> {}
    }
    restartTrickplayTimer()
}
private fun togglePlayback() {
    ...
    PlaybackState.IDLE -> when (mLocation) {
        ...
        PlaybackLocation.REMOTE -> {
            if (mCastSession != null && mCastSession!!.isConnected) {
                loadRemoteMedia(mSeekbar!!.progress, true)
            }
        }
        else -> {}
    }
    ...
}
override fun onPause() {
    ...
    mCastContext!!.sessionManager.removeSessionManagerListener(
                mSessionManagerListener!!, CastSession::class.java)
}
override fun onResume() {
    Log.d(TAG, "onResume() was called")
    mCastContext!!.sessionManager.addSessionManagerListener(
            mSessionManagerListener!!, CastSession::class.java)
    if (mCastSession != null && mCastSession!!.isConnected) {
        updatePlaybackLocation(PlaybackLocation.REMOTE)
    } else {
        updatePlaybackLocation(PlaybackLocation.LOCAL)
    }
    super.onResume()
}

Per il metodo updatePlayButton, modifica il valore della variabile isConnected:

private fun updatePlayButton(state: PlaybackState?) {
    ...
    val isConnected = (mCastSession != null
                && (mCastSession!!.isConnected || mCastSession!!.isConnecting))
    ...
}

Ora fai clic sul pulsante Pulsante Esegui di Android Studio, un triangolo verde rivolto verso destraEsegui per eseguire l'app sul tuo dispositivo mobile. Collegati al dispositivo di trasmissione e inizia a riprodurre un video. Dovresti vedere il video in riproduzione sul ricevitore.

7. Mini controller

Il controllo elenco di controllo del design di Google Cast richiede che tutte le app di Google Cast forniscano un mini controller che viene visualizzato quando l'utente esce dalla pagina dei contenuti corrente. Il mini controller offre accesso istantaneo e un promemoria visibile per la sessione di trasmissione in corso.

Illustrazione della parte inferiore di uno smartphone Android che mostra il miniplayer nell&#39;app Trasmetti video

L'SDK Cast fornisce una visualizzazione 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 alla fine di res/layout/player_activity.xml e 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 rivolto verso destraEsegui per eseguire l'app e trasmettere un video. Quando la riproduzione inizia sul ricevitore, il mini controller dovrebbe apparire in fondo a ogni attività. Puoi controllare la riproduzione da remoto utilizzando il mini controller. Se passi dall'attività di navigazione 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 che un'app mittente implementi i controlli multimediali da una notifica e dalla schermata di blocco.

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

L'SDK Cast fornisce un elemento MediaNotificationService per aiutare l'app del mittente a creare controlli multimediali per le notifiche e la schermata di blocco. Il servizio viene unito automaticamente al file manifest dell'app da Gradle.

MediaNotificationService verrà eseguito in background quando il mittente trasmette in streaming e mostrerà una notifica con una miniatura dell'immagine e i metadati dell'elemento di trasmissione in corso, un pulsante di riproduzione/pausa e un pulsante di arresto.

I controlli per le notifiche e la schermata di blocco possono essere attivati con il CastOptions durante l'inizializzazione di CastContext. I controlli multimediali per la notifica e la schermata di blocco sono attivi per impostazione predefinita. La funzionalità della schermata di blocco è attiva se le notifiche sono attive.

Modifica CastOptionsProvider e modifica 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 rivolto verso destraEsegui per eseguire l'app sul tuo dispositivo mobile. Trasmetti un video ed esci dall'app di esempio. Dovrebbe essere presente una notifica relativa al video attualmente in riproduzione sul ricevitore. Se blocchi il dispositivo mobile, ora nella schermata di blocco dovrebbero essere visualizzati i controlli per la riproduzione di contenuti multimediali sul dispositivo di trasmissione.

Illustrazione di uno smartphone Android che mostra i controlli multimediali nella schermata di blocco

9. Overlay introduttivo

L'elenco di controllo per la progettazione di Google Cast richiede che un'app mittente introduca il pulsante Trasmetti agli utenti esistenti per informarli che l'app mittente ora supporta la trasmissione e aiuta anche gli utenti che non hanno mai utilizzato Google Cast.

Illustrazione che mostra l&#39;overlay introduttivo di Trasmetti attorno al pulsante Trasmetti nell&#39;app Android Trasmetti video

L'SDK Cast fornisce una visualizzazione 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()
        }
    }
}

Ora aggiungi un elemento CastStateListener e richiama il metodo showIntroductoryOverlay quando è disponibile un dispositivo di trasmissione modificando il metodo onCreate e sostituisci 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 rimuovi l'app dal dispositivo. Poi, fai clic sul pulsante Pulsante Esegui di Android Studio, un triangolo verde rivolto verso destraEsegui per eseguire l'app sul tuo dispositivo mobile e dovresti vedere la finestra popup introduttiva (cancella i dati dell'app se la finestra popup non viene visualizzata).

10. Controller espanso

L'elenco di controllo per la progettazione di Google Cast richiede che un'app mittente fornisca il controllore espanso per i contenuti multimediali trasmessi. Il controller espanso è una versione a schermo intero del mini controller.

Illustrazione di un video in riproduzione su uno smartphone Android con il controller espanso sovrapposto

L'SDK Cast fornisce un widget per il controller espanso chiamato ExpandedControllerActivity. Si tratta di una classe astratta di cui devi creare una sottoclasse per aggiungere un pulsante Trasmetti.

Innanzitutto, crea un nuovo file di risorse del menu, chiamato expanded_controller.xml, affinché il controller espanso possa fornire 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. A questo punto, crea un nuovo file denominato ExpandedControlsActivity.kt nel pacchetto com.google.sample.cast.refplayer.expandedcontrols.

package com.google.sample.cast.refplayer.expandedcontrols

import android.view.Menu
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
import com.google.sample.cast.refplayer.R
import com.google.android.gms.cast.framework.CastButtonFactory

class ExpandedControlsActivity : ExpandedControllerActivity() {
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        super.onCreateOptionsMenu(menu)
        menuInflater.inflate(R.menu.expanded_controller, menu)
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
        return true
    }
}

Ora dichiara ExpandedControlsActivity in AndroidManifest.xml all'interno del tag application sopra OPTIONS_PROVIDER_CLASS_NAME:

<application>
    ...
    <activity
        android:name="com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:theme="@style/Theme.CastVideosDark"
        android:screenOrientation="portrait"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
        </intent-filter>
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.google.sample.cast.refplayer.VideoBrowserActivity"/>
    </activity>
    ...
</application>

Modifica CastOptionsProvider e 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 i contenuti multimediali remoti vengono caricati:

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 avviare l'app sul tuo dispositivo mobile e trasmettere un video. Dovresti vedere il controller espanso. Torna all'elenco dei video e, quando fai clic sul mini controller, il controller espanso verrà caricato di nuovo. Allontanati dall'app per vedere la notifica. Fai clic sull'immagine della notifica per caricare il controller espanso.

11. Aggiungere il supporto di Cast Connect

La libreria Cast Connect consente alle applicazioni di invio esistenti di comunicare con le applicazioni Android TV tramite il protocollo Cast. Cast Connect si basa sull'infrastruttura di Cast, con l'app Android TV che funge da ricevitore.

Dipendenze

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

LaunchOptions

Per avviare l'applicazione Android TV, indicata anche come Android Receiver, dobbiamo impostare il flag setAndroidReceiverCompatible su true nell'oggetto LaunchOptions. Questo oggetto LaunchOptions determina in che modo viene lanciato il ricevitore e viene passato a CastOptions restituito dalla classe CastOptionsProvider. Se imposti il flag sopra indicato su false, verrà avviato il ricevitore web per l'ID app definito nella Console per gli sviluppatori di Google Cast.

Nel file CastOptionsProvider.kt, aggiungi quanto segue al metodo getCastOptions:

import com.google.android.gms.cast.LaunchOptions
...
val launchOptions = LaunchOptions.Builder()
            .setAndroidReceiverCompatible(true)
            .build()
return new CastOptions.Builder()
        .setLaunchOptions(launchOptions)
        ...
        .build()

Imposta credenziali di avvio

Sul lato del mittente, puoi specificare CredentialsData per rappresentare chi partecipa alla sessione. credentials è una stringa che può essere definita dall'utente, purché la tua app ATV sia in grado di comprenderla. Il CredentialsData viene trasmesso alla tua app Android TV soltanto al momento del lancio o dell'iscrizione. Se lo imposti di nuovo mentre la connessione è attiva, non verrà trasmesso all'app Android TV.

Per impostare le credenziali di avvio, è necessario definire CredentialsData e passare all'oggetto LaunchOptions. Aggiungi il seguente codice al metodo getCastOptions nel file CastOptionsProvider.kt:

import com.google.android.gms.cast.CredentialsData
...

val credentialsData = CredentialsData.Builder()
        .setCredentials("{\"userId\": \"abc\"}")
        .build()
val launchOptions = LaunchOptions.Builder()
       ...
       .setCredentialsData(credentialsData)
       .build()

Impostare le credenziali su LoadRequest

Se l'app WebRicevitore e l'app Android TV gestiscono credentials in modo diverso, potresti dover definire credentials separati per ciascuna. Per farlo, 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 di destinazione a cui il mittente trasmette, l'SDK ora 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. Trova l'indirizzo IP del tuo dispositivo Android TV. Di solito è disponibile in Impostazioni > Rete e internet > (nome della rete a cui è connesso il dispositivo). A destra vengono visualizzati i dettagli e l'IP del dispositivo sulla rete.
  2. Utilizza l'indirizzo IP del tuo dispositivo per collegarlo tramite ADB utilizzando il terminale:
$ adb connect <device_ip_address>:5555
  1. Dalla finestra del terminale, vai alla cartella di primo livello con gli esempi del codelab che hai scaricato all'inizio di questo codelab. Ad esempio:
$ cd Desktop/android_codelab_src
  1. Installa il file .apk in questa cartella su Android TV eseguendo:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Ora dovresti riuscire a vedere un'app denominata Trasmetti video nel menu Le tue app sul tuo dispositivo Android TV.
  2. Torna al tuo progetto Android Studio e fai clic sul pulsante Esegui per installare ed eseguire l'app 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 dalle opzioni disponibili. Ora dovresti vedere l'app Android TV avviata sul tuo dispositivo Android TV e la riproduzione di un video dovrebbe consentirti di controllare la riproduzione utilizzando il telecomando di Android TV.

12. Personalizzare i widget di trasmissione

Puoi personalizzare i widget di trasmissione impostando i colori, lo stile dei pulsanti, il testo e l'aspetto delle miniature e scegliendo i tipi di pulsanti da visualizzare.

Aggiorna res/values/styles_castvideo.xml

<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
    ...
    <item name="mediaRouteTheme">@style/CustomMediaRouterTheme</item>
    <item name="castIntroOverlayStyle">@style/CustomCastIntroOverlay</item>
    <item name="castMiniControllerStyle">@style/CustomCastMiniController</item>
    <item name="castExpandedControllerStyle">@style/CustomCastExpandedController</item>
    <item name="castExpandedControllerToolbarStyle">
        @style/ThemeOverlay.AppCompat.ActionBar
    </item>
    ...
</style>

Dichiara i seguenti temi personalizzati:

<!-- Customize Cast Button -->
<style name="CustomMediaRouterTheme" parent="Theme.MediaRouter">
    <item name="mediaRouteButtonStyle">@style/CustomMediaRouteButtonStyle</item>
</style>
<style name="CustomMediaRouteButtonStyle" parent="Widget.MediaRouter.Light.MediaRouteButton">
    <item name="mediaRouteButtonTint">#EEFF41</item>
</style>

<!-- Customize Introductory Overlay -->
<style name="CustomCastIntroOverlay" parent="CastIntroOverlay">
    <item name="castButtonTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Button</item>
    <item name="castTitleTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Title</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Button" parent="android:style/TextAppearance">
    <item name="android:textColor">#FFFFFF</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Title" parent="android:style/TextAppearance.Large">
    <item name="android:textColor">#FFFFFF</item>
</style>

<!-- Customize Mini Controller -->
<style name="CustomCastMiniController" parent="CastMiniController">
    <item name="castShowImageThumbnail">true</item>
    <item name="castTitleTextAppearance">@style/TextAppearance.AppCompat.Subhead</item>
    <item name="castSubtitleTextAppearance">@style/TextAppearance.AppCompat.Caption</item>
    <item name="castBackground">@color/accent</item>
    <item name="castProgressBarColor">@color/orange</item>
</style>

<!-- Customize Expanded Controller -->
<style name="CustomCastExpandedController" parent="CastExpandedController">
    <item name="castButtonColor">#FFFFFF</item>
    <item name="castPlayButtonDrawable">@drawable/cast_ic_expanded_controller_play</item>
    <item name="castPauseButtonDrawable">@drawable/cast_ic_expanded_controller_pause</item>
    <item name="castStopButtonDrawable">@drawable/cast_ic_expanded_controller_stop</item>
</style>

13. Complimenti

Ora sai come abilitare la trasmissione di un'app video utilizzando i widget dell'SDK Cast su Android.

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