Obsługa przesyłania w aplikacji na Androida

1. Opis

Logo Google Cast

Dzięki tym ćwiczeniom z programowania dowiesz się, jak zmodyfikować istniejącą aplikację wideo na Androida, by przesyłać treści na urządzenie obsługujące Google Cast.

Co to jest Google Cast?

Google Cast pozwala użytkownikom przesyłać 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 pozwala rozszerzyć aplikację o sterowanie telewizorem lub systemem audio. Pakiet SDK Cast pozwala dodać niezbędne komponenty interfejsu zgodnie z listą kontrolną projektowania Google Cast.

Dostępna jest lista kontrolna projektu Google Cast, dzięki której korzystanie z Cast jest proste i przewidywalne na wszystkich obsługiwanych platformach.

Co będziemy tworzyć?

Po ukończeniu tych ćwiczeń z programowania będziesz mieć aplikację wideo na Androida umożliwiającą 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 Cast pozwalający wybrać urządzenie Google Cast.
  • Jak połączyć się z urządzeniem przesyłającym i uruchomić odbiornik multimediów.
  • Jak przesłać film.
  • Jak dodać minikontroler Cast do aplikacji
  • Obsługa powiadomień o multimediach i ustawień ekranu blokady
  • Jak dodać rozszerzony kontroler.
  • Jak dodać nakładkę początkową
  • Jak dostosować widżety Cast
  • Integracja z Cast Connect

Czego potrzebujesz

  • Najnowszy pakiet SDK na Androida.
  • Android Studio w wersji 3.2 lub nowszej,
  • 1 urządzenie mobilne z Androidem 4.1 lub nowszym Jelly Bean (poziom interfejsu API 16).
  • Kabel USB do transmisji danych do podłączenia urządzenia mobilnego do komputera, z którego korzystasz.
  • Urządzenie przesyłające Google Cast, takie jak Chromecast czy Android TV, z dostępem do internetu.
  • Telewizor lub monitor z wejściem HDMI.
  • Do testowania integracji Cast Connect wymagany jest Chromecast z Google TV, ale w pozostałej części Codelabs jest on opcjonalny. Jeśli go nie masz, możesz pominąć krok Dodawanie obsługi Cast Connect pod koniec tego samouczka.

Funkcja

  • Musisz mieć wiedzę w zakresie Kotlin i programowania na Androidzie.
  • Potrzebna będzie też wcześniejsza wiedza o oglądaniu telewizji :)

Jak będziesz korzystać z tego samouczka?

Przeczytaj tylko do końca Przeczytaj go i wykonaj ćwiczenia

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

Początkujący Średni Średni

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

Początkujący Średni Średni

2. Pobieranie przykładowego kodu

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

i rozpakuj pobrany plik ZIP.

3. Uruchamianie przykładowej aplikacji

ikona pary kompasów

Zobaczmy najpierw, jak wygląda kompletna przykładowa aplikacja. Ta aplikacja to podstawowy odtwarzacz wideo. Użytkownik może wybrać film z listy, a potem odtworzyć go lokalnie na urządzeniu lub przesłać na urządzenie przesyłające Google Cast.

Poniżej znajdziesz instrukcje dotyczące otwierania i uruchamiania ukończonej przykładowej aplikacji w Android Studio po pobraniu kodu:

Wybierz Importuj projekt na ekranie powitalnym lub opcje menu Plik > Nowy > Importuj projekt...

Wybierz katalog ikona folderuapp-done z folderu z przykładowym kodem 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 był widoczny, otwórz Ustawienia > Informacje o telefonie i 7 razy kliknij Numer kompilacji. Wróć do poprzedniego ekranu, otwórz System > Zaawansowane i u dołu kliknij Opcje programisty, a następnie Debugowanie USB, aby je włączyć.

Podłącz urządzenie z Androidem i kliknij przycisk Przycisk Uruchom w Android Studio, zielony trójkąt skierowany w prawoUruchom w Android Studio. Aplikacja wideo Przesyłanie filmów powinna pojawić się po kilku sekundach.

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

Wybierz film i kliknij przycisk odtwarzania.

Rozpocznie się odtwarzanie filmu na urządzeniu Google Cast.

Wyświetli się rozwinięty kontroler. Do sterowania odtwarzaniem możesz używać przycisku odtwarzania/wstrzymywania.

Wróć do listy filmów.

Minikontroler jest teraz widoczny u dołu ekranu. Grafika przedstawiająca telefon z Androidem, na którym działa aplikacja „Przesyłanie filmów” i minikontroler widoczny u dołu ekranu

Kliknij przycisk wstrzymania na minikontrolerze, aby wstrzymać odtwarzanie filmu na odbiorniku. Kliknij przycisk odtwarzania na minikontrolerze, aby wznowić odtwarzanie filmu.

Kliknij przycisk ekranu głównego na urządzeniu mobilnym. Pociągnij w dół powiadomienia. Powinno pojawić się powiadomienie o sesji przesyłania.

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

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

Najczęstsze pytania

4. Przygotuj projekt początkowy

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

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

  • aplikacja nadawca działa na urządzeniu mobilnym lub laptopie,
  • aplikacja odbiornika działa na urządzeniu Google Cast;

Możesz teraz utworzyć kampanię wykorzystującą projekt początkowy w Android Studio:

  1. Wybierz katalog ikona folderuapp-start z pobranego przykładowego kodu (wybierz Importuj projekt na ekranie powitalnym lub opcję 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, zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację i poznać interfejs użytkownika.

Projektowanie aplikacji

Aplikacja pobiera listę filmów ze zdalnego serwera WWW i udostępnia listę, którą użytkownik może przeglądać. Użytkownicy mogą wybrać film, aby zobaczyć szczegóły, lub odtworzyć go lokalnie na urządzeniu mobilnym.

Aplikacja składa się z 2 głównych aktywności: VideoBrowserActivity i LocalPlayerActivity. Aby można było zintegrować funkcję Google Cast, działania muszą odziedziczyć element AppCompatActivity lub element nadrzędny FragmentActivity. To ograniczenie istnieje, ponieważ musimy dodać MediaRouteButton (udostępnioną w bibliotece pomocy MediaRouter) jako element MediaRouteActionProvider. Będzie to działać tylko wtedy, gdy aktywność dziedziczy uprawnienia ze wspomnianych wyżej klas. Biblioteka obsługi MediaRouter zależy od biblioteki pomocy AppCompat, która zawiera wymagane klasy.

VideoBrowserActivity

Ta aktywność zawiera Fragment (VideoBrowserFragment). Podstawą tej listy jest ArrayAdapter (VideoListAdapter). Lista filmów i powiązane z nimi metadane są przechowywane na serwerze zdalnym jako plik 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 z nimi ścieżki tekstowe (na potrzeby napisów). Obiekt MediaItem jest przekazywany między działaniami, więc MediaItem ma metody pozwalające przekonwertować go na Bundle i odwrotnie.

Gdy program wczytujący tworzy listę MediaItems, przekazuje ją do funkcji VideoListAdapter, która następnie prezentuje listę MediaItems w elemencie VideoBrowserFragment. Użytkownikowi wyświetla się lista miniatur filmów wraz z krótkim opisem każdego z nich. Po wybraniu elementu odpowiedni MediaItem jest konwertowany na Bundle i jest przekazywany do LocalPlayerActivity.

LocalPlayerActivity

W ramach tego działania wyświetlane są metadane konkretnego filmu oraz użytkownik może odtworzyć film lokalnie na urządzeniu mobilnym.

Aktywność obejmuje element VideoView, niektóre elementy sterujące multimediami oraz obszar tekstowy do wyświetlania opisu wybranego filmu. Odtwarzacz zakrywa górną część ekranu, pozostawiając miejsce na szczegółowy opis filmu. Użytkownik może włączać i wstrzymywać odtwarzanie oraz przewijać je lokalnie.

Zależności

Korzystamy z AppCompatActivity, dlatego potrzebujemy biblioteki pomocy AppCompat. Do zarządzania listą filmów i asynchronicznego pobierania obrazów z listy używamy biblioteki Volley.

Najczęstsze pytania

5. Dodawanie przycisku Cast

Grafika przedstawiająca górną część telefonu z Androidem, na którym działa aplikacja Cast Video. Przycisk Cast w prawym górnym rogu ekranu

Aplikacja obsługująca Cast wyświetla przycisk Cast przy każdym działaniu. Gdy klikniesz przycisk Cast, pojawi się lista urządzeń przesyłających, 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 rozpocznie lub wznowi odtwarzanie na tym urządzeniu. W każdej chwili podczas 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 go od niego podczas wykonywania dowolnej czynności w aplikacji zgodnie z opisem na liście kontrolnej projektowania 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 kompilacje projektu nie zawierają błędów.

Zdarzenie inicjujące

Platforma Cast zawiera globalny obiekt typu singleton CastContext, który koordynuje wszystkie interakcje Cast.

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

Gdy tworzysz własną aplikację obsługującą Cast, musisz się zarejestrować jako deweloper Cast, a potem uzyskać identyfikator aplikacji. 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” w pliku AndroidManifest.xml aplikacji:

<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 VideoBrowserActivity onCreate:

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 ten sam mechanizm inicjowania do interfejsu LocalPlayerActivity.

Przycisk Cast

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

Edytuj plik res/menu/browse.xml i dodaj element 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() obiektu VideoBrowserActivity, używając CastButtonFactory do podłączenia MediaRouteButton do platformy przesyłania:

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 tabeli LocalPlayerActivity w podobny sposób.

Kliknij przycisk Przycisk Uruchom w Android Studio, zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. Na pasku działań aplikacji powinien być widoczny przycisk Cast. Gdy go klikniesz, pojawi się lista urządzeń przesyłających w Twojej sieci lokalnej. Wykrywaniem urządzeń zarządza automatycznie CastContext. Wybierz urządzenie przesyłające, a przykładowa aplikacja odbiornika zostanie na nim załadowana. Możesz przechodzić między aktywnością przeglądania a aktywnością lokalnego odtwarzacza, a stan przycisku przesyłania jest zsynchronizowany.

Nie obsługujemy odtwarzania multimediów, więc nie możesz jeszcze odtwarzać filmów na urządzeniu przesyłającym. Aby rozłączyć, kliknij przycisk Cast.

6. Przesyłanie treści wideo

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

Udostępnimy przykładową aplikację, aby umożliwić zdalne odtwarzanie filmów na urządzeniach przesyłających. Aby to zrobić, musimy wsłuchiwać się w różne zdarzenia generowane przez platformę przesyłania.

Przesyłanie multimediów

Aby odtworzyć multimedia na urządzeniu przesyłającym, musisz wykonać te czynności:

  1. Utwórz obiekt MediaInfo, który będzie modelował element multimedialny.
  2. Podłącz urządzenie przesyłające i uruchom aplikację odbiornika.
  3. Wczytaj obiekt MediaInfo do odbiornika i odtwórz treści.
  4. Śledzenie stanu multimediów.
  5. Wysyłaj polecenia odtwarzania do odbiornika na podstawie interakcji użytkownika.

Krok 2 z poprzedniej sekcji został już wykonany. Krok 3 można łatwo wykonać za pomocą platformy przesyłania. Etap 1 sprowadza się do mapowania jednego obiektu na drugi. Element MediaInfo jest zrozumiały dla platformy przesyłania, a MediaItem to kodowanie elementu multimedialnego w naszej aplikacji. Obiekt MediaItem możemy łatwo zmapować na obiekt MediaInfo.

W przykładowej aplikacji LocalPlayerActivity można już odróżnić odtwarzanie lokalne i zdalne przy użyciu tej wyliczenia:

private var mLocation: PlaybackLocation? = null

enum class PlaybackLocation {
    LOCAL, REMOTE
}

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

W tym ćwiczeniu z programowania nie ma potrzeby dokładnego zrozumienia, jak działa cała przykładowa logika odtwarzacza. Pamiętaj, że konieczne będzie zmodyfikowanie odtwarzacza w aplikacji, aby rozpoznawał 2 miejsca odtwarzania w podobny sposób.

W tej chwili lokalny odtwarzacz jest zawsze w lokalnym stanie odtwarzania, ponieważ nie ma jeszcze żadnych informacji o stanach przesyłania. Musimy aktualizować interfejs odpowiednio do zmian stanu w platformie przesyłania. Jeśli na przykład rozpocznie się przesyłanie, musimy zatrzymać odtwarzanie lokalne i wyłączyć niektóre elementy sterujące. Jeśli w trakcie aktywności zatrzymasz przesyłanie, musimy przełączyć się na odtwarzanie lokalne. Aby to zrobić, musimy nasłuchiwać różnych zdarzeń generowanych przez platformę przesyłania.

Zarządzanie sesjami przesyłania

W przypadku platformy przesyłania sesja obejmuje etapy połączenia z urządzeniem, uruchamiania (lub dołączania), łączenia się z aplikacją odbierającą i inicjowania kanału sterowania multimediami (jeśli ma to zastosowanie). Kanał sterowania multimediami służy do wysyłania i odbierania wiadomości przez platformę przesyłania z odtwarzacza.

Sesja przesyłania rozpocznie się automatycznie, gdy użytkownik wybierze urządzenie, klikając przycisk Cast, i zakończy się automatycznie, gdy użytkownik rozłączy się. Ponowne łączenie z sesją odbiorcy 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 ramach aktywności w usłudze LocalPlayerActivity będziemy wdzięczni za informację o połączeniu lub rozłączeniu urządzenia przesyłającego, abyśmy mogli przełączyć się na lokalny odtwarzacz lub z niego zrezygnować. Pamiętaj, że łączność może zostać zakłócona nie tylko przez wystąpienie aplikacji uruchomionej na urządzeniu mobilnym, ale także przez inne wystąpienie tej aplikacji uruchomionej na innym urządzeniu mobilnym.

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

Musimy zarejestrować detektor sesji i zainicjować kilka zmiennych, których użyjemy w tym działaniu. 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

RemoteMediaClient w pakiecie SDK Cast udostępnia zestaw wygodnych interfejsów API do zarządzania zdalnym odtwarzaniem multimediów na odbiorniku. W przypadku CastSession, który obsługuje odtwarzanie multimediów, pakiet SDK automatycznie utworzy wystąpienie elementu RemoteMediaClient. Można go uzyskać, wywołując metodę getRemoteMediaClient() w instancji CastSession. Dodaj do aplikacji LocalPlayerActivity te metody, aby wczytać 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 istniejące metody, aby korzystać z logiki sesji przesyłania do 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 Uruchom 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 być odtwarzany na odbiorniku.

7. Minikontroler

Lista kontrolna projektu przesyłania wymaga, aby wszystkie aplikacje Cast miały minikontroler, który pojawia się, gdy użytkownik opuści stronę, na której obecnie znajdują się treści. Minikontroler zapewnia natychmiastowy dostęp i widoczne przypomnienie o bieżącej sesji przesyłania.

Ilustracja przedstawiająca dolną część telefonu z Androidem, na której widać miniodtwarzacz w aplikacji Cast Video

Pakiet SDK przesyłania udostępnia widok niestandardowy MiniControllerFragment. Możesz go dodać do pliku układu aplikacji z aktywnościami, w których chcesz pokazać minikontroler.

Dodaj tę definicję fragmentu na dole stron res/layout/player_activity.xml i 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"/>

Kliknij przycisk Przycisk Uruchom w Android Studio, zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację i przesłać film. Gdy rozpocznie się odtwarzanie na odbiorniku, u dołu każdej aktywności powinien pojawić się minikontroler. Odtwarzaniem zdalnym możesz sterować za pomocą minikontrolera. Jeśli przełączasz się między aktywnością przeglądania a aktywnością w odtwarzaczu lokalnym, stan minikontrolera powinien być zsynchronizowany ze stanem odtwarzania multimediów przez odbiornik.

8. Powiadomienia i ekran blokady

Lista kontrolna projektu Google Cast wymaga, aby aplikacja nadawcy wdrożyła opcje sterowania multimediami z powiadomienia i ekranu blokady.

Grafika przedstawiająca telefon z Androidem, na którym widać opcje sterowania multimediami w obszarze powiadomień.

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

Podczas przesyłania treści urządzenie MediaNotificationService będzie działać w tle i będzie wyświetlać powiadomienie z miniaturą obrazu, metadanymi i bieżącym elementem przesyłania, przyciskami odtwarzania/wstrzymywania oraz przyciskami zatrzymywania.

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

Edytuj CastOptionsProvider i zmień implementację getCastOptions, 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()
}

Kliknij przycisk Przycisk Uruchom w Android Studio, zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. Prześlij film i otwórz przykładową aplikację. Powinno pojawić się powiadomienie o filmie aktualnie odtwarzanym na odbiorniku. Zablokuj urządzenie mobilne, dzięki czemu na ekranie blokady powinny być widoczne elementy sterujące odtwarzaniem multimediów na urządzeniu przesyłającym.

Grafika przedstawiająca telefon z Androidem, na którym widać opcje sterowania multimediami na ekranie blokady.

9. Nakładka wprowadzająca

Lista kontrolna projektu Google Cast wymaga, aby aplikacja nadawcy poinformowała użytkowników o tym, że przycisk Cast może przesyłać treści, i poinformuje ich, że aplikacja nadawcy obsługuje przesyłanie, a także pomaga nowym użytkownikom korzystającym z Google Cast.

Ilustracja przedstawiająca początkową nakładkę Cast wokół przycisku Cast w aplikacji Prześlij filmy na Androida

Pakiet SDK Cast udostępnia widok niestandardowy (IntroductoryOverlay), dzięki któremu można wyróżnić przycisk przesyłania, gdy użytkownik zobaczy go po raz pierwszy. Dodaj do adresu 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łaj metodę showIntroductoryOverlay, gdy urządzenie przesyłające będzie dostępne. Aby to zrobić, zmodyfikuj metodę onCreate i zastąp metody onResume oraz onPause, aby dopasować je do tych ustawień:

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, zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. Powinna się pojawić nakładkę z wprowadzeniem (jeśli się nie wyświetla, wyczyść dane aplikacji).

10. Rozwinięty kontroler

Lista kontrolna projektu Google Cast wymaga, aby aplikacja nadawcy zapewniła rozszerzony kontroler do przesyłanych multimediów. Rozwinięty kontroler to pełnoekranowa wersja minikontrolera.

Ilustracja przedstawiająca film odtwarzany na telefonie z Androidem, na którym nałożony jest rozwinięty kontroler.

Pakiet SDK Cast zawiera widżet ExpandedControllerActivity dla rozwiniętego kontrolera. Jest to klasa abstrakcyjna, którą musisz podzielić, aby dodać przycisk Cast.

Najpierw utwórz nowy plik zasobów menu o nazwie expanded_controller.xml, aby rozwinięty kontroler zawierał 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 nowy plik o nazwie ExpandedControlsActivity.kt w pakiecie 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
    }
}

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>

Edytuj CastOptionsProvider i 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 multimediów zdalnych:

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, zielony trójkąt skierowany w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym i przesłać film. Powinien być widoczny rozwinięty kontroler. Wróć do listy filmów. Gdy klikniesz minikontroler, rozwinięty kontroler zostanie ponownie wczytany. Aby zobaczyć powiadomienie, wyjdź z aplikacji. Kliknij obraz powiadomienia, aby wczytać rozwinięty kontroler.

11. Dodawanie obsługi Cast Connect

Biblioteka Cast Connect umożliwia istniejącym aplikacjom nadawcy komunikowanie się z aplikacjami na Androida TV przez protokół Cast. Cast Connect działa na bazie infrastruktury przesyłania, a aplikacja na Androida TV działa jako odbiornik.

Zależności

Uwaga: aby zaimplementować Cast Connect, play-services-cast-framework musi mieć wartość 19.0.0 lub wyższą.

LaunchOptions

Aby uruchomić aplikację Android TV (określaną też jako odbiornik Androida), musimy ustawić w obiekcie LaunchOptions flagę setAndroidReceiverCompatible na true. Ten obiekt LaunchOptions określa sposób uruchamiania odbiornika i jest przekazywany do metody CastOptions zwracanej przez klasę CastOptionsProvider. Ustawienie wskazanej wyżej flagi na wartość false spowoduje uruchomienie odbiornika internetowego dla zdefiniowanego identyfikatora aplikacji w konsoli programisty Cast.

W pliku CastOptionsProvider.kt dodaj do metody getCastOptions 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ć CredentialsData, aby reprezentował, kto dołącza do sesji. Ciąg credentials jest ciągiem zdefiniowanym przez użytkownika, pod warunkiem że jest on zrozumiały dla aplikacji ATV. Element CredentialsData jest przekazywany do aplikacji na Androida TV tylko w czasie jej uruchomienia lub dołączenia. Jeśli skonfigurujesz je ponownie po połączeniu z siecią, nie zostaną one przekazane do aplikacji na Androida TV.

Aby ustawić dane logowania do uruchamiania, należy zdefiniować CredentialsData i przekazać 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 żądaniu LoadRequest

Jeśli aplikacja Web Font i aplikacja na Androida TV obsługują różne parametry credentials, może być konieczne zdefiniowanie dla każdej z nich osobnego elementu credentials. Aby rozwiązać ten problem, w pliku LocalPlayerActivity.kt w sekcji funkcji loadRemoteMedia dodaj ten kod:

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 automatycznie określa dane logowania, których będzie używać w bieżącej sesji.

Testowanie Cast Connect

Instalowanie pakietu APK na Androida TV na urządzeniu Chromecast z Google TV

  1. Znajdź adres IP swojego urządzenia z Androidem TV. Zwykle znajdziesz je w sekcji Ustawienia > Sieć i internet > (nazwa sieci, z którą połączone jest urządzenie). Po prawej stronie wyświetlą się szczegóły oraz adres IP urządzenia połączony z siecią.
  2. Użyj adresu IP urządzenia, aby połączyć się z nim przez ADB za pomocą terminala:
$ adb connect <device_ip_address>:5555
  1. W oknie terminala przejdź do folderu najwyższego poziomu na przykłady ćwiczeń z programowania pobrane na początku tego ćwiczenia z programowania. Na przykład:
$ cd Desktop/android_codelab_src
  1. Zainstaluj plik .apk w tym folderze na Androidzie TV, uruchamiając polecenie:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Aplikacja powinna być teraz widoczna pod nazwą Przesyłaj filmy w menu Twoje aplikacje na urządzeniu z Androidem TV.
  2. Wróć do projektu 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 z dostępnych opcji wybierz swoje urządzenie z Androidem TV. Aplikacja Android TV powinna pojawić się 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, tekst i wygląd miniatur, a także wybierając typy wyświetlanych przycisków.

Aktualizuj sieć 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 w aplikacji wideo za pomocą widżetów Cast SDK na Androida.

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