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 SDK Google Cast umożliwia rozszerzenie aplikacji o możliwość sterowania telewizorem lub systemem audio. Pakiet Cast SDK umożliwia dodawanie niezbędnych komponentów interfejsu użytkownika na podstawie listy kontrolnej dotyczącej projektowania 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 będziemy budować?

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 SDK Google Cast do przykładowej aplikacji wideo
  • Jak dodać przycisk przesyłania, aby wybrać urządzenie Google Cast.
  • Jak połączyć się z urządzeniem Cast i uruchomić odbiornik multimediów.
  • Jak przesyłać filmy.
  • Jak dodać do aplikacji kontroler Cast Mini
  • Jak obsługiwać powiadomienia multimedialne i sterowanie na ekranie blokady.
  • Jak dodać rozszerzony kontroler.
  • Jak utworzyć nakładkę wprowadzającą.
  • Jak dostosować widżety przesyłania.
  • 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 przesyłania danych, który umożliwia połączenie urządzenia mobilnego z komputerem programistycznym.
  • Urządzenie 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

  • Musisz mieć już doświadczenie w programowaniu w Kotlinie i na Androida.
  • Wymagamy też wcześniejszej wiedzy na temat oglądania telewizji. :)

Jak będziesz korzystać z tego samouczka?

Tylko przeczytać Przeczytać i wykonać ćwiczenia

Jak oceniasz tworzenie aplikacji na Androida?

Początkujący Średnio zaawansowany Zaawansowany

Jak oceniasz oglądanie telewizji?

Początkujący Średnio zaawansowany Zaawansowany

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 kompasu

Najpierw zobaczmy, jak wygląda gotowa 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 wykonaj te instrukcje, aby otworzyć i uruchomić gotową przykładową aplikację w Android Studio:

Na ekranie powitalnym wybierz Importuj projekt lub kliknij menu Plik > Nowy > Importuj projekt....

Wybierz katalog ikona folderuapp-done z folderu przykładowego kodu 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 – w Androidzie 4.2 i nowszych ekran Opcje programisty jest domyślnie ukryty. Aby go wyświetlić, otwórz Ustawienia > Informacje o telefonie i kliknij Numer kompilacji 7 razy. Wróć do poprzedniego ekranu, kliknij System > Zaawansowane, a potem u dołu kliknij Opcje programisty. Następnie kliknij Debugowanie przez USB, aby włączyć tę opcję.

Podłącz urządzenie z Androidem i kliknij przycisk Przycisk Wygeneruj w Android Studio – zielony trójkąt skierowany w prawoUruchom w Android Studio. Po kilku sekundach powinna pojawić się 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. Ilustracja telefonu z Androidem z aplikacją „Przesyłaj filmy” oraz minikontrolerem widocznym u dołu ekranu

Aby wstrzymać odtwarzanie filmu na odbiorniku, kliknij przycisk pauzy na minikontrolerze. Aby ponownie odtworzyć film, kliknij przycisk odtwarzania w miniodtwarzaczu.

Kliknij przycisk ekranu głównego urządzenia mobilnego. Po przesunięciu palcem w dół powinno się wyświetlić powiadomienie o sesji przesyłania.

Zablokuj telefon, a po jego odblokowaniu zobaczysz na ekranie blokady powiadomienie z prośbą o sterowanie odtwarzaniem multimediów lub zatrzymywanie przesyłania.

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

Najczęstsze pytania

4. Przygotowanie projektu startowego

Ilustracja przedstawiająca telefon z Androidem z otwartą aplikacją „Cast Videos”

Musimy dodać obsługę Google Cast do pobranej przez Ciebie aplikacji startowej. Oto terminologia dotycząca Google Cast, której będziemy używać w tym ćwiczeniu z programowania:

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

Teraz możesz zacząć tworzyć projekt początkowy przy użyciu Android Studio:

  1. Wybierz katalog ikona folderuapp-start z pobranego przykładowego kodu (na ekranie powitalnym kliknij Import Project (Importuj projekt) lub w menu Plik > Nowy > Importuj projekt...).
  2. Kliknij przycisk Przycisk „Synchronizuj projekt z Gradle” w Android Studio Synchronizuj projekt z plikami Gradle.
  3. Kliknij przycisk Przycisk Wygeneruj w Android Studio – zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację i poznać interfejs.

Projektowanie aplikacji

Aplikacja pobiera listę filmów z zdalnego 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 działań: VideoBrowserActivityLocalPlayerActivity. Aby zintegrować funkcję Google Cast, aktywności muszą dziedziczyć albo z poziomu AppCompatActivity, albo z poziomu elementu nadrzędnego FragmentActivity. To ograniczenie istnieje, ponieważ musimy dodać obiekt MediaRouteButton (znajdujący się w bibliotece pomocy MediaRouter) jako element MediaRouteActionProvider. Działa ono tylko wtedy, gdy aktywność jest dziedziczona ze wspomnianych klas. Biblioteka pomocy MediaRouter zależy od biblioteki pomocy AppCompat, która udostępnia wymagane klasy.

VideoBrowserActivity

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

Obiekt MediaItem modeluje film i powiązane z nim metadane, takie jak tytuł, opis, URL strumienia, URL obrazów pomocniczych i powiązane ścieżki tekstowe (np. napisy). Obiekt MediaItem jest przekazywany między działaniami, więc MediaItem ma metody pomocnicze, które umożliwiają jego konwersję na obiekt Bundle i na odwrót.

Gdy ładowarka tworzy listę MediaItems, przekazuje ją do VideoListAdapter, który następnie przedstawia listę MediaItemsVideoBrowserFragment. 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 zajmuje górną część ekranu, pozostawiając miejsce na szczegółowy opis filmu poniżej. Użytkownik może odtwarzać/wstrzymywać lub przewijać lokalne odtwarzanie filmów.

Zależności

Używamy AppCompatActivity, więc 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 Przesyłanie

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 w ramach każdej aktywności. 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 nadawczym, wybór urządzenia przesyłającego spowoduje rozpoczęcie lub wznowienie odtwarzania na tym urządzeniu. W dowolnym momencie sesji przesyłania użytkownik może kliknąć przycisk Cast i zatrzymać przesyłanie aplikacji na urządzenie przesyłające. Użytkownik musi mieć możliwość połączenia się z urządzeniem przesyłającym lub odłączenia się od niego podczas wykonywania dowolnej czynności w aplikacji, zgodnie z opisem w spisie kontrolnym Google Cast Design.

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 potwierdzić kompilacje projektu bez błędów.

Zdarzenie inicjujące

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

Aby zainicjować pojedynczy obiekt CastContext, musisz zaimplementować interfejs OptionsProvider, który umożliwia podanie wartości CastOptions. Najważniejszą opcją jest identyfikator aplikacji odbiorczej, który służy do filtrowania wyników wykrywania urządzeń Cast i uruchamiania aplikacji odbiorczej po rozpoczęciu sesji Cast.

Podczas tworzenia własnej aplikacji z obsługą Cast musisz zarejestrować się jako deweloper Cast, a potem uzyskać identyfikator aplikacji. W tym samouczku użyjemy przykładowego identyfikatora aplikacji.

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

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

Zainicjuj CastContext w metodzie VideoBrowserActivity onCreate w sposób leniwy:

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 aktywności (za pomocą elementu menu ActionBar lub Toolbar), musisz najpierw dodać do menu odpowiedni element menu.

Zmodyfikuj plik res/menu/browse.xml i dodaj element MediaRouteActionProvider do 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
}

Zastąp onCreateOptionsMenu w elemencie LocalPlayerActivity w podobny sposób.

Kliknij przycisk Przycisk Wygeneruj w Android Studio – zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. Na pasku czynności aplikacji powinien pojawić się przycisk Cast. Po jego kliknięciu zobaczysz listę urządzeń Cast w Twojej sieci lokalnej. Wykrywanie urządzeń jest zarządzane automatycznie przez CastContext. Wybierz urządzenie przesyłania treści, a na urządzeniu przesyłania treści zostanie załadowana przykładowa aplikacja odbiornika. 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. Aby odłączyć urządzenie, kliknij przycisk Cast.

6. Przesyłanie treści wideo

Ilustracja przedstawiająca telefon z Androidem z otwartą aplikacją „Cast Videos”

Rozszerzymy przykładową aplikację o możliwość zdalnego odtwarzania filmów na urządzeniu przesyłającym. Aby to zrobić, musimy nasłuchiwać różnych zdarzeń generowanych przez platformę Cast.

Przesyłanie multimediów

Jeśli chcesz odtworzyć multimedia na urządzeniu z Castem, 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. śledzić 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. Łatwy sposób na platformę Cast. Krok 1 polega na mapowaniu jednego obiektu na inny. MediaInfo jest zrozumiały dla platformy Cast, a MediaItem to element opakowania w naszej aplikacji. Możemy łatwo mapować MediaItem na MediaInfo.

Przykładowa aplikacja LocalPlayerActivity już rozróżnia odtwarzanie lokalne i zdalne za pomocą tej listy wyliczeniowej:

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.

W tej chwili lokalny odtwarzacz jest zawsze w lokalnym stanie odtwarzania, bo 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 lokalne i wyłączyć niektóre elementy sterujące. Podobnie, jeśli przestaniemy przesyłać strumieniowo, gdy jesteśmy w tej aktywności, musimy przejść do odtwarzania lokalnego. Aby to zrobić, musimy nasłuchiwać różnych zdarzeń generowanych przez platformę Cast.

Zarządzanie sesją przesyłania

W ramach platformy Cast sesja Cast obejmuje połączenie z urządzeniem, uruchomienie (lub dołączenie), połączenie z aplikacją odbiorczą i w odpowiednich przypadkach zainicjowanie kanału sterowania multimediami. Kanał sterowania multimediami to sposób, w jaki platforma Cast wysyła i odbiera wiadomości od odtwarzacza multimedialnego.

Sesja przesyłania rozpocznie się automatycznie, gdy użytkownik wybierze urządzenie za pomocą przycisku przesyłania, i zatrzyma się automatycznie, gdy użytkownik się rozłączy. Ponowne nawiązanie połączenia z sesją odbiorczą z powodu problemów z siecią jest również obsługiwane automatycznie przez pakiet SDK Cast.

Dodajmy SessionManagerListener do elementu 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 przypadku aktywności LocalPlayerActivity chcemy otrzymywać informacje o połączeniu się z urządzeniem Cast lub rozłączeniu z nim, abyśmy mogli przełączyć się na lokalnego odtwarzacza lub z niego. Pamiętaj, że połączenie może zostać zakłócone nie tylko przez wystąpienie aplikacji uruchomionej na urządzeniu mobilnym, ale również przez inne wystąpienie Twojej (lub innej) aplikacji uruchomionej na innym urządzeniu mobilnym.

Obecnie aktywna sesja jest dostępna jako SessionManager.getCurrentSession(). Sesje są tworzone i wyłączane automatycznie w odpowiedzi na interakcje użytkownika z oknami 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)
        }
    }
    ...
}

Wczytywanie multimediów

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 automatycznie utworzy 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 przypadku metody 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 z Cast 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 bieżącej 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 SDK Google Cast udostępnia widok niestandardowy MiniControllerFragment, który można dodać do pliku układu aplikacji w przypadku aktywności, w których chcesz wyświetlać minikontroler.

Dodaj na dole fragmentu kodu res/layout/player_activity.xml i res/layout/video_browser.xml definicję tego 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 przełączysz się między aktywnością przeglądania a aktywizacją 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 telefonu z Androidem, na którym widać opcje sterowania multimediami w obszarze powiadomień

Pakiet Cast SDK udostępnia interfejs MediaNotificationService, który pomaga aplikacji nadawcy tworzyć elementy sterujące multimediami na ekranie powiadomień i ekranie blokady. Usługa jest automatycznie scalana z plikiem manifestu aplikacji przez Gradle.

Aplikacja MediaNotificationService będzie działać w tle, gdy nadawca będzie przesyłać treści, i wyświetli powiadomienie z miniaturą obrazu oraz metadanymi dotyczącymi przesyłanego obecnie elementu, a także przyciski odtwarzania/pauzowania i zatrzymania.

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

Zmień kod CastOptionsProvider i implementację getCastOptions, aby pasowały 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()
}

Kliknij przycisk Przycisk Wygeneruj w Android Studio – zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. 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 z elementami sterowania multimediami na ekranie blokady

9. Wstępna nakładka

Lista kontrolna dotycząca projektu Google Cast wymaga, aby aplikacja wysyłająca wprowadziła użytkowników w przycisk Cast, aby poinformować ich, że aplikacja obsługuje teraz przesyłanie treści, a także aby pomóc 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 jest on po raz pierwszy wyświetlany użytkownikom. Dodaj do 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 zmienną CastStateListener i wywołaj metodę showIntroductoryOverlay, gdy dostępne jest urządzenie Cast. W tym celu zmodyfikuj metodę onCreate i zastąp metody onResumeonPause, aby pasowały do tego:

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 Wygeneruj w Android Studio – zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. Powinien pojawić się wstępny ekran. Jeśli nie, wyczyść dane aplikacji.

10. Rozwinięty kontroler

Lista kontrolna projektu Google Cast wymaga, by aplikacja wysyłająca dostarczała rozwinięty kontroler do przesyłanych multimediów. Rozwinięty kontroler to wersja minikontrolera na pełnym ekranie.

Ilustracja przedstawiająca film odtwarzany na telefonie z Androidem z rozwiniętym kontrolerem nałożonym na ekranie

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 zasobu menu o nazwie expanded_controller.xml dla rozszerzonego kontrolera, aby udostępnić przycisk Cast:

<?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 utwórz w pakiecie com.google.sample.cast.refplayer.expandedcontrols 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 tagu AndroidManifest.xml w tagu application nad tagiem 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>

Edytuj CastOptionsProvider oraz zmień NotificationOptions i CastMediaOptions, aby ustawić aktywność docelową na 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świetlać ExpandedControlsActivity po wczytaniu 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 Wygeneruj w Android Studio – zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym i przesłać film. Zobaczysz rozwinięty kontroler. Wróć do listy filmów i kliknij miniaturę kontrolera, aby ponownie załadować rozszerzony kontroler. Aby zobaczyć powiadomienie, zamknij aplikację. Kliknij obraz powiadomienia, aby wczytać rozwinięty kontroler.

11. Dodawanie obsługi Cast Connect

Biblioteka Cast Connect umożliwia istniejącym aplikacjom nadawczym komunikowanie się z aplikacją Androida TV za pomocą 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 „Prawda”. Ten obiekt LaunchOptions określa sposób uruchamiania odbiorcy i jest przekazywany do funkcji CastOptions zwróconej przez klasę CastOptionsProvider. Ustawienie powyższej flagi na false uruchamia odbiornik internetowy dla zdefiniowanego identyfikatora aplikacji w konsoli programisty 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()

Ustawianie danych logowania do uruchamiania

Po stronie nadawcy możesz określić wartość CredentialsData, aby wskazać, kto dołącza do sesji. 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 do metody getCastOptions w pliku CastOptionsProvider.kt ten kod:

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, konieczne może być zdefiniowanie osobnych wartości credentials dla każdej z nich. Aby zadbać o to, dodaj ten kod w pliku LocalPlayerActivity.kt w funkcji loadRemoteMedia:

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

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

Testowanie Cast Connect

Instalowanie pliku APK Android TV na Chromecastzie 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 zobaczysz szczegóły i 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 najwyższego poziomu folderu przykładów kodu pobranego na początku tego ćwiczenia. Na przykład:
$ cd Desktop/android_codelab_src
  1. Zainstaluj plik .apk z tego folderu w Androidzie TV, uruchamiając polecenie:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Na urządzeniu z Androidem TV w menu Twoje aplikacje powinieneś/powinnaś zobaczyć aplikację o nazwie Cast Videos.
  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 się uruchomić na urządzeniu z Androidem TV, a odtwarzanie filmu powinno umożliwić sterowanie odtwarzaniem za pomocą pilota Android TV.

12. Dostosuj widżety Cast

Widżety przesyłania możesz dostosować, ustawiając kolory, styl przycisków, tekstu i miniatur oraz wybierając typy przycisków do wyświetlania.

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

Wiesz już, jak włączyć przesyłanie aplikacji wideo, korzystając z widżetów Cast SDK na Androida.

Więcej informacji znajdziesz w przewodniku dla deweloperów dotyczącym nadawcy na Androidzie.