Obsługa przesyłania w aplikacji na Androida

1. Omówienie

Logo Google Cast

Z tego Codelab dowiesz się, jak zmodyfikować istniejącą aplikację do filmów na Androida, aby przesyłać treści na urządzenie z Google Cast.

Co to jest Google Cast?

Google Cast umożliwia użytkownikom przesyłanie treści z urządzenia mobilnego na telewizor. Dzięki temu użytkownicy mogą używać swoich urządzeń mobilnych jako pilota do odtwarzania multimediów na telewizorze.

Pakiet Google Cast SDK umożliwia rozszerzenie aplikacji o sterowanie telewizorem lub systemem audio. SDK Cast pozwala dodać niezbędne komponenty interfejsu zgodnie z listą kontrolną projektu Google Cast.

Lista kontrolna dotycząca projektowania Google Cast została przygotowana, aby zapewnić użytkownikom prostotę i przewidywalność korzystania z Casta na wszystkich obsługiwanych platformach.

Co utworzymy?

Po ukończeniu tego Codelab będziesz mieć aplikację wideo na Androida, która umożliwia przesyłanie filmów na urządzenie obsługujące Google Cast.

Czego się nauczysz

  • Jak dodać pakiet Google Cast SDK do przykładowej aplikacji wideo.
  • Jak dodać przycisk przesyłania umożliwiający wybór urządzenia Google Cast?
  • Jak połączyć się z urządzeniem Cast i uruchomić odbiornik multimediów.
  • Jak przesyłać filmy.
  • Jak dodać minikontroler Cast do aplikacji.
  • Jak obsługiwać powiadomienia multimedialne i sterowanie na ekranie blokady.
  • Jak dodać rozszerzony kontroler.
  • Jak umieścić wstępny nakład.
  • Jak dostosować widżety Cast.
  • Jak przeprowadzić integrację z Cast Connect

Czego potrzebujesz

  • Najnowszy pakiet SDK na Androida.
  • Android Studio w wersji 3.2 lub nowszej,
  • Jedno urządzenie mobilne z Androidem 4.1 lub nowszym (poziom interfejsu API 16).
  • Kabel USB do podłączenia urządzenia mobilnego do komputera programisty.
  • Urządzenie przesyłające Google Cast, takie jak Chromecast lub Android TV, skonfigurowane z dostępem do internetu.
  • telewizor lub monitor z wejściem HDMI.
  • Chromecast z Google TV jest wymagany do przetestowania integracji Cast Connect, ale nie jest wymagany w pozostałych częściach tego Codelaba. Jeśli nie masz takiego urządzenia, możesz pominąć krok Dodaj obsługę Cast Connect pod koniec tego samouczka.

Doświadczenie

  • Niezbędna jest do tego wiedza o programowaniu aplikacji na Androida i Kotlin.
  • Musisz też znać zasady oglądania telewizji :)

Jak wykorzystasz ten samouczek?

Tylko przeczytać Przeczytać i wykonać ćwiczenia

Jak oceniasz swoje doświadczenia z tworzeniem aplikacji na Androida?

Początkujący Średnio zaawansowany Zaawansowany

Jak oceniasz swoje wrażenia z oglądania telewizji?

Początkujący Poziom średnio zaawansowany Biegły

2. Pobieranie przykładowego kodu

Możesz pobrać cały przykładowy kod na swój komputer...

i rozpakuj pobrany plik ZIP.

3. Uruchamianie przykładowej aplikacji

ikona pary kompasów

Najpierw zobaczmy, jak wygląda ukończona przykładowa aplikacja. Aplikacja jest podstawowym odtwarzaczem wideo. Użytkownik może wybrać film z listy, a następnie odtworzyć go lokalnie na urządzeniu lub przesłać na urządzenie z Google Cast.

Po pobraniu kodu postępuj zgodnie z tymi instrukcjami, aby otworzyć i uruchomić pełną aplikację w Android Studio:

Wybierz Importuj projekt na ekranie powitalnym lub kliknij Plik > Nowe > Importuj projekt...

W folderze z przykładowym kodem wybierz katalog ikona folderuapp-done i kliknij OK.

Kliknij Plik >Przycisk „Synchronizuj projekt z Gradle” w Android Studio Synchronizuj projekt z plikami Gradle.

Włącz debugowanie USB na urządzeniu z Androidem – na urządzeniach z Androidem 4.2 i nowszym ekran Opcje programisty jest domyślnie ukryty. Aby je włączyć, wybierz Ustawienia > Informacje o telefonie i 7 razy kliknij Numer kompilacji. Wróć do poprzedniego ekranu i przejdź do sekcji System > Zaawansowane i na dole kliknij Opcje programisty, a następnie kliknij Debugowanie USB, by włączyć tę funkcję.

Podłącz urządzenie z Androidem i kliknij przycisk Przycisk Uruchom w Android Studio z zielonym trójkątem wskazującym w prawoUruchom w Android Studio. Po kilku sekundach powinna się wyświetlić aplikacja do przesyłania filmów Prześlij filmy.

Kliknij przycisk Cast w aplikacji wideo i wybierz urządzenie Google Cast.

Wybierz film i kliknij przycisk odtwarzania.

Film zacznie się odtwarzać na urządzeniu z Google Cast.

Wyświetli się rozwinięty kontroler. Możesz sterować odtwarzaniem za pomocą przycisku odtwarzania/wstrzymania.

Wróć do listy filmów.

U dołu ekranu pojawi się minikontroler. Grafika przedstawiająca telefon z Androidem, na którym działa „Przesyłanie filmów”. z minikontrolerem widocznym u dołu ekranu

Aby wstrzymać odtwarzanie filmu na odbiorniku, kliknij przycisk pauzy na minikontrolerze. Kliknij przycisk odtwarzania na minikontrolerze, aby wznowić odtwarzanie filmu.

Kliknij przycisk ekranu głównego urządzenia mobilnego. Przeciągnij w dół powiadomienia, aby wyświetlić powiadomienie dotyczące sesji przesyłania.

Zablokuj telefon, a gdy go odblokujesz, na ekranie blokady powinno pojawić się powiadomienie umożliwiające sterowanie odtwarzaniem multimediów lub zatrzymanie przesyłania.

Wróć do aplikacji wideo i kliknij przycisk Cast, aby zatrzymać przesyłanie na urządzeniu Google Cast.

Najczęstsze pytania

4. Przygotowywanie projektu początkowego

Grafika przedstawiająca telefon z Androidem, na którym działa „Przesyłanie filmów”. aplikacja

Musimy dodać obsługę Google Cast do pobranej przez Ciebie aplikacji startowej. Oto kilka terminów dotyczących Google Cast, których użyjemy w tym Codelab:

  • aplikacja nadawcy działa na urządzeniu mobilnym lub laptopie,
  • na urządzeniu Google Cast działa aplikacja odbiornikowa.

Teraz możesz tworzyć aplikację na podstawie projektu startowego w Android Studio:

  1. Wybierz katalog ikona folderuapp-start z pliku z próbnym kodem (na ekranie powitalnym wybierz Importuj projekt lub kliknij Plik > Nowy > Importuj projekt…).
  2. Kliknij przycisk Przycisk „Synchronizuj projekt z Gradle” w Android Studio Synchronizuj projekt z plikami Gradle.
  3. Kliknij przycisk Przycisk Uruchom w Android Studio z zielonym trójkątem wskazującym w prawoUruchom, aby uruchomić aplikację i poznać interfejs.

Projektowanie aplikacji

Aplikacja pobiera listę filmów z usługowego serwera internetowego i udostępnia ją użytkownikowi. Użytkownicy mogą wybrać film, aby wyświetlić jego szczegóły lub odtworzyć go lokalnie na urządzeniu mobilnym.

Aplikacja składa się z 2 głównych aktywności: VideoBrowserActivity i LocalPlayerActivity. Aby zintegrować funkcję Google Cast, Aktywności muszą dziedziczyć element AppCompatActivity lub jego element nadrzędny FragmentActivity. To ograniczenie istnieje, ponieważ musielibyśmy dodać MediaRouteButton (dostępny w bibliotece obsługi MediaRouter) jako MediaRouteActionProvider. Działa to tylko wtedy, gdy aktywność dziedziczy po wymienionych powyżej klasach. Biblioteka obsługi MediaRouter zależy od biblioteki obsługi AppCompat, która udostępnia wymagane klasy.

VideoBrowserActivity

Ta aktywność zawiera Fragment (VideoBrowserFragment). Ta lista jest obsługiwana przez ArrayAdapter (VideoListAdapter). Lista filmów i powiązanych z nimi metadanych jest hostowana na serwerze zdalnym w pliku JSON. AsyncTaskLoader (VideoItemLoader) pobiera ten plik JSON i przetwarza go, aby utworzyć listę obiektów MediaItem.

Obiekt MediaItem modeluje film i powiązane z nim metadane, takie jak tytuł, opis, adres URL strumienia, adres URL obrazów pomocniczych i powiązane ścieżki tekstowe (na potrzeby napisów). Obiekt MediaItem jest przekazywany między działaniami, dlatego MediaItem ma metody pomocnicze do konwertowania go na obiekt Bundle i odwrotnie.

Gdy program ładujący tworzy listę elementów MediaItems, przekazuje ją do funkcji VideoListAdapter, która następnie wyświetla listę MediaItems w elemencie VideoBrowserFragment. Użytkownik widzi listę miniatur filmów z krótkim opisem każdego filmu. Po wybraniu elementu odpowiadający mu element MediaItem jest przekształcany w element Bundle i przekazywany do elementu LocalPlayerActivity.

LocalPlayerActivity

To działanie wyświetla metadane konkretnego filmu i pozwala użytkownikowi odtworzyć go lokalnie na urządzeniu mobilnym.

Aktywność zawiera VideoView, niektóre elementy sterujące multimediami oraz obszar tekstowy z opisem wybranego filmu. Odtwarzacz zasłania górną część ekranu, pozostawiając miejsce na szczegółowy opis filmu. Użytkownik może odtwarzać i wstrzymywać filmy albo przewijać je lokalnie.

Zależności

Ponieważ używamy AppCompatActivity, potrzebujemy biblioteki AppCompat. Do zarządzania listą filmów i asyncjonalnego pobierania obrazów na tę listę używamy biblioteki Volley.

Najczęstsze pytania

5. Dodawanie przycisku Cast

Ilustracja górnej części telefonu z Androidem z uruchomioną aplikacją Cast Video; przycisk Cast w prawym górnym rogu ekranu

Aplikacja obsługująca Cast wyświetla przycisk Cast podczas każdego działania. Po kliknięciu przycisku przesyłania wyświetla się lista urządzeń przesyłania, które użytkownik może wybrać. Jeśli użytkownik odtwarzał treści lokalnie na urządzeniu nadawcy, wybranie urządzenia przesyłającego spowoduje uruchomienie lub wznowienie odtwarzania na tym urządzeniu. W dowolnym momencie podczas sesji przesyłania użytkownik może kliknąć przycisk Cast, aby zatrzymać przesyłanie aplikacji na urządzenie Cast. Podczas każdej aktywności w aplikacji użytkownik musi mieć możliwość łączenia się z urządzeniem przesyłającym lub jego rozłączenia, co opisano na liście kontrolnej projektowania w Google Cast.

Zależności

Zaktualizuj plik build.gradle aplikacji, aby uwzględnić niezbędne zależności biblioteki:

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"
}

Zsynchronizuj projekt, aby sprawdzić, czy kompilacja przebiega bez błędów.

Zdarzenie inicjujące

Platforma Cast ma globalny obiekt pojedynczy CastContext, który koordynuje wszystkie interakcje z Cast.

Musisz wdrożyć interfejs OptionsProvider, aby dostarczyć CastOptions niezbędny do zainicjowania singletonu CastContext. Najważniejszą opcją jest identyfikator aplikacji odbiornika, który służy do filtrowania wyników wykrywania urządzeń przesyłających i uruchamiania aplikacji odbiornika po rozpoczęciu sesji przesyłania.

Gdy tworzysz własną aplikację obsługującą Google Cast, musisz zarejestrować się jako programista Cast, a potem uzyskać dla niej identyfikator. W tym ćwiczeniu z programowania użyjemy przykładowego identyfikatora aplikacji.

Dodaj nowy plik CastOptionsProvider.kt do pakietu com.google.sample.cast.refplayer projektu:

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

Teraz zadeklaruj OptionsProvider w tagu „application” pliku aplikacji AndroidManifest.xml:

<meta-data
    android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
    android:value="com.google.sample.cast.refplayer.CastOptionsProvider" />

Leniwie zainicjuj CastContext w metodzie 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)
}

Dodaj tę samą logikę inicjowania do interfejsu LocalPlayerActivity.

Przycisk Cast

Po zainicjowaniu CastContext musimy dodać przycisk przesyłania, aby umożliwić użytkownikowi wybranie urządzenia do przesyłania. Przycisk Cast jest implementowany przez MediaRouteButton z biblioteki MediaRouter. Podobnie jak w przypadku każdej ikony działania, którą możesz dodać do swojej aktywności (za pomocą elementu menu ActionBar lub Toolbar), musisz najpierw dodać do menu odpowiedni element menu.

Edytuj plik res/menu/browse.xml i dodaj pozycję MediaRouteActionProvider w menu przed elementem ustawień:

<item
    android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
    app:showAsAction="always"/>

Zastąp metodę onCreateOptionsMenu() klasy VideoBrowserActivity, używając do tego metody CastButtonFactory, aby połączyć klasę MediaRouteButton z ramą 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
}

W podobny sposób możesz zastąpić onCreateOptionsMenu w plikach LocalPlayerActivity.

Kliknij przycisk Przycisk Uruchom w Android Studio z zielonym trójkątem wskazującym w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. Na pasku czynności aplikacji powinien pojawić się przycisk przesyłania. Gdy go klikniesz, zobaczysz listę urządzeń Cast w Twojej sieci lokalnej. Wykrywanie urządzeń jest zarządzane automatycznie przez CastContext. Wybierz urządzenie przesyłające. Aplikacja odbiornika załaduje się na to urządzenie. Możesz przełączać się między przeglądaniem a odtwarzaniem lokalnym, a stan przycisku Cast będzie zachowany w zsynchronizowany sposób.

Nie udostępniliśmy obsługi odtwarzania multimediów, więc nie można jeszcze odtwarzać filmów na urządzeniu Cast. Kliknij przycisk Cast, by odłączyć urządzenie.

6. Przesyłanie treści wideo

Grafika przedstawiająca telefon z Androidem, na którym działa „Przesyłanie filmów”. aplikacja

Rozwiniemy przykładową aplikację, aby można było odtwarzać filmy zdalnie na urządzeniu obsługującym Cast. Aby to zrobić, musimy nasłuchiwać różnych zdarzeń generowanych przez platformę Cast.

Przesyłanie multimediów

Ogólnie, jeśli chcesz odtworzyć multimedia na urządzeniu przesyłającym, musisz wykonać te czynności:

  1. Utwórz obiekt MediaInfo, który modeluje element multimedialny.
  2. Połącz się z urządzeniem przesyłającym i uruchom aplikację odbiornika.
  3. Wczytaj obiekt MediaInfo do odbiornika i odtwórz treści.
  4. sprawdzać stan multimediów,
  5. Wysyłanie poleceń odtwarzania do odbiornika na podstawie interakcji użytkownika.

Krok 2 został już wykonany w poprzedniej sekcji. Krok 3 jest łatwy do wykonania dzięki platformie Cast. Etap 1 oznacza mapowanie jednego obiektu na drugi. MediaInfo to zrozumiały sposób dla platformy Cast, a MediaItem to kod elementu multimedialnego aplikacji. możemy łatwo zmapować MediaItem na MediaInfo.

Przykładowa aplikacja LocalPlayerActivity odróżnia odtwarzanie lokalne od odtwarzania zdalnego za pomocą tego wyliczenia:

private var mLocation: PlaybackLocation? = null

enum class PlaybackLocation {
    LOCAL, REMOTE
}

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

W tym ćwiczeniu nie musisz dokładnie rozumieć, jak działa cała przykładowa logika odtwarzacza. Pamiętaj, że trzeba zmodyfikować odtwarzacz multimediów w aplikacji, aby w podobny sposób rozpoznawał 2 miejsca odtwarzania.

Obecnie odtwarzacz lokalny jest zawsze w stanie odtwarzania lokalnego, ponieważ nie wie jeszcze nic o stanach przesyłania. Musimy zaktualizować interfejs użytkownika na podstawie przejść między stanami, które występują w ramach platformy Cast. Jeśli na przykład zaczniemy przesyłać treści, musimy zatrzymać odtwarzanie na urządzeniu lokalnym i wyłączyć niektóre elementy sterujące. Podobnie, jeśli zakończymy przesyłanie w trakcie tego działania, będziemy musieli przełączyć się na odtwarzanie lokalne. Aby to uwzględnić, musimy nasłuchiwać różnych zdarzeń generowanych przez platformę Cast.

Zarządzanie sesją przesyłania

W przypadku platformy przesyłania sesja przesyłania obejmuje łączenie się z urządzeniem, uruchamianie (lub dołączanie), nawiązywanie połączenia z aplikacją odbiornika oraz w razie potrzeby inicjowanie kanału sterowania multimediami. Kanał sterowania multimediami służy do wysyłania i odbierania wiadomości z odtwarzacza multimedialnego odbiornika.

Sesja przesyłania rozpocznie się automatycznie, gdy użytkownik wybierze urządzenie, klikając przycisk Cast, i zatrzyma się automatycznie, gdy użytkownik się rozłączy. Ponowne łączenie się z sesją odbiornika z powodu problemów z siecią jest również obsługiwane automatycznie przez pakiet SDK Cast.

Dodajmy SessionManagerListener do 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()
       }
   }
}

W ramach aktywności w usłudze LocalPlayerActivity chcemy otrzymać powiadomienie, gdy połączymy się z urządzeniem przesyłającym lub rozłączy się ono z urządzeniem przesyłającym, aby umożliwić przełączenie się na lokalny odtwarzacz lub z niego. Pamiętaj, że połączenie może zostać przerwane nie tylko przez instancję aplikacji uruchomionej na urządzeniu mobilnym, ale też przez inną instancję tej samej aplikacji (lub innej) uruchomionej na innym urządzeniu mobilnym.

Bieżąca aktywna sesja jest dostępna jako SessionManager.getCurrentSession(). Sesje są tworzone i zamykane automatycznie w odpowiedzi na interakcje użytkownika z dialogami przesyłania.

Musimy zarejestrować detektor sesji i zainicjować pewne zmienne, których będziemy używać w ćwiczeniu. Zmień metodę LocalPlayerActivity onCreate na:

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)
        }
    }
    ...
}

Wczytuję multimedia

W pakiecie Cast SDK interfejs RemoteMediaClient udostępnia zestaw wygodnych interfejsów API do zdalnego zarządzania odtwarzaniem multimediów na odbiorniku. W przypadku CastSession, który obsługuje odtwarzanie multimediów, pakiet SDK utworzy automatycznie instancję RemoteMediaClient. Można go wywołać, wywołując metodę getRemoteMediaClient() w obiekcie CastSession. Dodaj do LocalPlayerActivity te metody, aby załadować aktualnie wybrany film na odbiorniku:

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()
    }
}

Teraz zaktualizuj różne metody, aby używać logiki sesji przesyłania do obsługi zdalnego odtwarzania:

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()
}

W metodzie updatePlayButton zmień wartość zmiennej isConnected:

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

Teraz kliknij przycisk Przycisk Wygeneruj w Android Studio – zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. Połącz się z urządzeniem przesyłającym i zacznij odtwarzać film. Film powinien się odtwarzać na odbiorniku.

7. Mini kontroler

Lista kontrolna dotycząca projektowania aplikacji Cast wymaga, aby wszystkie aplikacje Cast zawierały miniaturowy kontroler, który pojawia się, gdy użytkownik przejdzie z obecnej strony treści. Minikontroler zapewnia natychmiastowy dostęp i widoczne przypomnienie o bieżącej sesji przesyłania.

Ilustracja dolna część telefonu z Androidem, na której widać miniodtwarzacz w aplikacji Cast Videos

Pakiet Cast SDK udostępnia widok niestandardowy MiniControllerFragment, który można dodać do pliku układu aplikacji z działaniami, w których chcesz wyświetlać minikontroler.

Na dole formularzy res/layout/player_activity.xml i res/layout/video_browser.xml dodaj tę definicję fragmentu:

<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"/>

Kliknij przycisk Przycisk Wygeneruj w Android Studio – zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację i przesłać film. Gdy odtwarzanie rozpocznie się na odbiorniku, na dole każdej aktywności powinien pojawić się minikontroler. Odtwarzaniem możesz sterować za pomocą minikontrolera. Jeśli przechodzisz między aktywnością związaną z przeglądaniem a aktywnością lokalnego odtwarzacza, stan minikontrolera powinien być zsynchronizowany ze stanem odtwarzania multimediów na odbiorniku.

8. Powiadomienia i ekran blokady

Lista kontrolna Google Cast wymaga, aby aplikacja nadawcza implementowała elementy sterujące multimediami w powiadomieniu i na ekranie blokady.

Ilustracja przedstawiająca telefon z Androidem z elementami sterowania multimediami w obszarze powiadomień

Pakiet SDK Cast udostępnia pakiet MediaNotificationService, który pomaga aplikacji wysyłającej tworzyć opcje sterowania multimediami dla powiadomień i ekranu blokady. Usługa jest automatycznie scalana z plikiem manifestu aplikacji przez Gradle.

Gdy nadawca będzie przesyłać, MediaNotificationService będzie działać w tle i wyświetli powiadomienie z miniaturą obrazu i metadanymi dotyczącymi aktualnie przesyłanego elementu, a także z przyciskiem odtwarzania/wstrzymywania i przyciskiem zatrzymania.

Ustawienia powiadomień i ekranu blokady można włączyć za pomocą elementu CastOptions podczas inicjowania aplikacji CastContext. Opcje sterowania multimediami na ekranie powiadomień i ekranie blokady są domyślnie włączone. Funkcja ekranu blokady jest włączona, dopóki włączone są powiadomienia.

Edytuj CastOptionsProvider i zmień implementację getCastOptions tak, aby pasowała do tego kodu:

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()
}

Aby uruchomić aplikację na urządzeniu mobilnym, kliknij przycisk Przycisk Uruchom w Android Studio z zielonym trójkątem wskazującym w prawoUruchom. Przekaż film i wyjdź z próbnej aplikacji. Na urządzeniu odbiorczym powinno pojawić się powiadomienie o odtwarzanym filmie. Zablokuj urządzenie mobilne, a na ekranie blokady powinny pojawić się elementy sterujące odtwarzaniem multimediów na urządzeniu przesyłającym.

Ilustracja przedstawiająca telefon z Androidem i elementy sterujące multimediami na ekranie blokady

9. Wstępna nakładka

Lista kontrolna dotycząca projektowania w Google Cast wymaga, by aplikacja nadawcy przedstawiła przycisk Cast dotychczasowym użytkownikom i poinformowała ich, że obsługuje ona przesyłanie, a także pomaga użytkownikom, którzy dopiero zaczynają korzystać z Google Cast.

Ilustracja przedstawiająca wprowadzenie do Cast w aplikacji Cast Videos na Androida

Pakiet Cast SDK udostępnia widok niestandardowy (IntroductoryOverlay), który można wykorzystać do wyróżnienia przycisku Cast, gdy zostanie on wyświetlony użytkownikom po raz pierwszy. Dodaj do pliku VideoBrowserActivity ten kod:

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()
        }
    }
}

Teraz dodaj CastStateListener i wywołuj metodę showIntroductoryOverlay, gdy urządzenie przesyłające jest dostępne. W tym celu zmodyfikuj metodę onCreate i zastąp metody onResume oraz onPause, tak aby odpowiadały tym wartościom:

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!!)
}

Wyczyść dane aplikacji lub usuń ją z urządzenia. Następnie kliknij przycisk Przycisk Uruchom w Android Studio z zielonym trójkątem wskazującym w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. Powinna wyświetlić się nakładka wprowadzająca (jeśli się nie wyświetla, wyczyść dane aplikacji).

10. Rozwinięty kontroler

Lista kontrolna Google Cast wymaga, aby aplikacja nadawcza udostępniała rozszerzony kontroler dla przesyłanych multimediów. Rozwinięty kontroler to wersja minikontrolera na pełnym ekranie.

Grafika przedstawiająca film odtwarzany na telefonie z Androidem, na który nakłada się rozwinięty kontroler.

Pakiet Cast SDK udostępnia widżet rozszerzonego kontrolera o nazwie ExpandedControllerActivity. To jest klasa abstrakcyjna, którą musisz umieścić w podklasie, aby dodać przycisk Cast.

Najpierw utwórz nowy plik zasobów menu o nazwie expanded_controller.xml, by rozwinięty kontroler udostępniał przycisk przesyłania:

<?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>

Utwórz nowy pakiet expandedcontrols w pakiecie com.google.sample.cast.refplayer. Następnie w pakiecie com.google.sample.cast.refplayer.expandedcontrols utwórz nowy plik o nazwie ExpandedControlsActivity.kt.

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

Teraz zadeklaruj ExpandedControlsActivity w elemencie AndroidManifest.xml w tagu application nad 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>

Zmień plik CastOptionsProvider, aby w ustawieniach NotificationOptionsCastMediaOptions ustawić jako aktywność docelową 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()
}

Zaktualizuj metodę LocalPlayerActivity loadRemoteMedia, aby wyświetlić ExpandedControlsActivity po załadowaniu zdalnych multimediów:

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())
}

Kliknij przycisk Przycisk Uruchom w Android Studio z zielonym trójkątem wskazującym w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym i przesłać film. Powinien się wyświetlić rozwinięty kontroler. Wróć do listy filmów i kliknij miniaturę kontrolera, aby ponownie wyświetlić rozszerzony kontroler. Aby zobaczyć powiadomienie, opuść aplikację. Kliknij obraz powiadomienia, aby wczytać rozwinięty kontroler.

11. Dodawanie obsługi Cast Connect

Biblioteka Cast Connect pozwala dotychczasowym aplikacjom nadawcy komunikować się z aplikacjami na Androida TV przy użyciu protokołu Cast. Cast Connect opiera się na infrastrukturze Cast, a aplikacja na Androida TV jest odbiornikiem.

Zależności

Uwaga: aby zaimplementować Cast Connect, musisz mieć play-services-cast-framework w wersji 19.0.0 lub nowszej.

LaunchOptions

Aby uruchomić aplikację na Androida TV, zwaną też odbiornikiem Androida, musimy ustawić w obiekcie LaunchOptions flagę setAndroidReceiverCompatible na wartość Prawda. Ten obiekt LaunchOptions określa sposób uruchamiania odbiorcy i jest przekazywany do funkcji CastOptions zwróconej przez klasę CastOptionsProvider. Ustawienie wspomnianego powyżej parametru na false spowoduje uruchomienie odbiornika internetowego dla zdefiniowanego identyfikatora aplikacji w konsoli deweloperskiej Google Cast.

W pliku CastOptionsProvider.kt dodaj do metody getCastOptions ten kod:

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

Ustaw dane logowania do uruchamiania

Po stronie nadawcy możesz określić, kto dołącza do sesji, określając CredentialsData. credentials to ciąg znaków, który może być zdefiniowany przez użytkownika, o ile tylko aplikacja ATV go rozumie. Wartość CredentialsData jest przekazywana do aplikacji na Androida TV tylko podczas uruchamiania lub dołączania. Jeśli ustawisz go ponownie, gdy jesteś połączony, nie zostanie ono przekazane do aplikacji Android TV.

Aby móc ustawić LaunchingCredentials, musisz zdefiniować obiekt CredentialsData i przekazać go do obiektu LaunchOptions. Dodaj ten kod do metody getCastOptions w pliku CastOptionsProvider.kt:

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

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

Ustawianie danych logowania w LoadRequest

Jeśli aplikacja Web Receiver i aplikacja na Androida TV obsługują credentials inaczej, może być konieczne zdefiniowanie osobnych wartości credentials dla każdej z nich. Aby to zrobić, dodaj ten kod do pliku LocalPlayerActivity.kt w ramach funkcji loadRemoteMedia:

remoteMediaClient.load(MediaLoadRequestData.Builder()
       ...
       .setCredentials("user-credentials")
       .setAtvCredentials("atv-user-credentials")
       .build())

W zależności od aplikacji odbiorcy, do której wysyłane są treści, pakiet SDK będzie teraz automatycznie określać, których danych logowania użyć w bieżącej sesji.

Testuję Cast Connect

Instalowanie pliku APK Androida TV na Chromecaście z Google TV

  1. Znajdź adres IP urządzenia z Androidem TV. Zwykle jest ona dostępna w sekcji Ustawienia > Sieć i internet > (Nazwa sieci, z którą połączone jest urządzenie). Po prawej stronie pojawią się szczegóły oraz adres IP urządzenia w sieci.
  2. Użyj adresu IP, by połączyć się z urządzeniem przez ADB:
$ adb connect <device_ip_address>:5555
  1. W oknie terminala przejdź do folderu najwyższego poziomu z przykładowymi ćwiczeniami z programowania pobranymi na początku tego ćwiczenia. Na przykład:
$ cd Desktop/android_codelab_src
  1. Zainstaluj plik .apk w tym folderze na Androidzie TV, uruchamiając:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. W menu Twoje aplikacje na urządzeniu z Androidem TV powinna być teraz widoczna aplikacja o nazwie Przesyłaj filmy.
  2. Wróć do projektu w Android Studio i kliknij przycisk Uruchom, aby zainstalować i uruchomić aplikację nadawcy na fizycznym urządzeniu mobilnym. W prawym górnym rogu kliknij ikonę przesyłania i wybierz urządzenie z Androidem TV z dostępnych opcji. Aplikacja Android TV powinna być widoczna na urządzeniu z Androidem TV, a odtworzenie filmu powinno umożliwić sterowanie odtwarzaniem za pomocą pilota Android TV.

12. Dostosuj widżety Cast

Możesz dostosować widżety przesyłania, ustawiając kolory, styl przycisków, tekstu i wyglądu miniatur, a także określając typy przycisków, które mają się wyświetlać.

Zaktualizuj: 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>

Zadeklaruj te motywy niestandardowe:

<!-- 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. Gratulacje

Teraz już wiesz, jak włączyć przesyłanie aplikacji wideo za pomocą widżetów pakietu SDK przesyłania na Androidzie.

Więcej informacji znajdziesz w przewodniku dla programistów Android Sender.