Obsługa przesyłania w aplikacji na Androida

1. Omówienie

Logo Google Cast

Dzięki tym ćwiczeniom w Codelabs dowiesz się, jak zmodyfikować istniejącą aplikację wideo na Androida, aby móc przesyłać treści na urządzenie obsługujące 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ą projektowania Google Cast.

Znajdziesz tam listę kontrolną projektowania Google Cast, która ułatwia i przewidywalność korzystania z Google Cast na wszystkich obsługiwanych platformach.

Co utworzymy?

Po ukończeniu tego ćwiczenia w Codelabs będziesz mieć aplikację wideo na Androida, która pozwoli przesyłać filmy 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 przesyłającym i uruchomić odbiornik multimediów.
  • Jak przesyłać filmy.
  • Jak dodać minikontroler Cast do aplikacji.
  • Jak obsługiwać powiadomienia o multimediach i ustawienia ekranu blokady.
  • Jak dodać rozwinięty kontroler.
  • Jak utworzyć nakładkę wprowadzającą.
  • 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 Jelly Bean lub nowszym (poziom 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.
  • Do testowania integracji Cast Connect wymagany jest Chromecast z Google TV, ale w pozostałych ćwiczeniach z programowania jest opcjonalny. 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.
  • Wymagamy też wcześniejszej wiedzy na temat oglądania telewizji. :)

Jak wykorzystasz ten samouczek?

Tylko do przeczytania Przeczytaj go i wykonaj ćwiczenia

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

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

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 i odtworzyć go lokalnie na urządzeniu lub przesłać na urządzenie przesyłające 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...

Wybierz katalog ikona folderuapp-done z folderu przykładowego kodu i kliknij OK.

Kliknij Plik > Android Studio – synchronizacja projektu z Gradle przycisk Synchronizacja projektu 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 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ę pojawić aplikacja wideo o nazwie Przesyłaj filmy.

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. Możesz sterować odtwarzaniem za pomocą przycisku odtwarzania/wstrzymania.

Wróć do listy filmów.

U dołu ekranu pojawił się teraz minikontroler. Ilustracja telefonu z Androidem, na którym działa „Przesyłanie filmów” z minikontrolerem widocznym u dołu ekranu

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

Kliknij przycisk ekranu głównego na urządzeniu mobilnym. 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. Przygotowywanie projektu początkowego

Ilustracja telefonu z Androidem, na którym działa „Przesyłanie filmów” aplikacja

Musisz 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 nadawca działa na urządzeniu mobilnym lub laptopie,
  • aplikacja odbiornika na urządzeniu Google Cast).

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 Android Studio – synchronizacja projektu z Gradle przycisk 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 zapoznać się z interfejsem użytkownika.

Projektowanie aplikacji

Aplikacja pobiera listę filmów ze zdalnego serwera WWW i udostępnia użytkownikowi listę do przeglądania. Użytkownicy mogą wybrać film, aby wyświetlić 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ż 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 element Fragment (VideoBrowserFragment). Wspiera tę listę przez regułę ArrayAdapter (VideoListAdapter). Lista filmów i powiązane z nimi metadane są przechowywane na serwerze zdalnym w postaci 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, więc funkcja MediaItem udostępnia metody użytkowe, które pozwalają przekształcić go w Bundle i odwrotnie.

Gdy moduł ładujący tworzy listę 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ące mu pole MediaItem jest przekształcane w wartość Bundle i przekazywana do funkcji LocalPlayerActivity.

LocalPlayerActivity

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

Aktywność obejmuje element VideoView, niektóre opcje sterowania multimediami oraz pole tekstowe 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

Używamy biblioteki AppCompatActivity, dlatego potrzebujemy biblioteki pomocy AppCompat. Do zarządzania listą filmów i asynchronicznego pobierania obrazów do listy służy biblioteka Volley.

Najczęstsze pytania

5. Dodawanie przycisku Cast

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

Aplikacja obsługująca Cast wyświetla przycisk Cast podczas każdego działania. Po kliknięciu tego przycisku wyświetla 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 spowoduje uruchomienie 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. 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 potwierdzić kompilacje projektu bez błędów.

Zdarzenie inicjujące

Platforma Cast ma globalny obiekt typu singleton – CastContext, który koordynuje wszystkie interakcje 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 kolumnie „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 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

Urządzenie CastContext jest już zainicjowane. Teraz trzeba dodać przycisk Cast, aby użytkownik mógł wybrać urządzenie przesyłające. Przycisk Cast jest zaimplementowany przez MediaRouteButton z biblioteki pomocy MediaRouter. Tak jak w przypadku każdej ikony działania, którą możesz dodać do aktywności (za pomocą ActionBar lub Toolbar), musisz najpierw dodać do menu odpowiednią pozycję z 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() metody VideoBrowserActivity, używając CastButtonFactory, aby połączyć MediaRouteButton z platformą 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 Uruchom w Android Studio z zielonym trójkątem wskazującym w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. Na pasku działań aplikacji powinien pojawić się przycisk Cast. Kliknięcie go spowoduje wyświetlenie urządzeń przesyłających w sieci lokalnej. Wykrywaniem urządzeń zarządza automatycznie CastContext. Wybierz urządzenie przesyłające. Aplikacja odbiornika załaduje się na to urządzenie. Możesz przechodzić między aktywnością związaną z przeglądaniem a aktywnością lokalnego odtwarzacza, a stan przycisku przesyłania jest zsynchronizowany.

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

6. Przesyłanie treści wideo

Ilustracja telefonu z Androidem, na którym działa „Przesyłanie filmów” aplikacja

Rozszerzymy przykładową aplikację o możliwość zdalnego odtwarzania filmów na urządzeniu przesyłającym. Aby to zrobić, musisz 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 modelujący 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. Śledź stan multimediów.
  5. Wysyłaj polecenia odtwarzania do odbiornika na podstawie interakcji użytkownika.

Krok 2 w poprzedniej sekcji został już wykonany. Krok 3. Łatwy sposób na platformę 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
}

Z tego ćwiczenia z programowania nie musisz dokładnie wiedzieć, jak dział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 na podstawie przejść stanów, które mają miejsce w ramach platformy Cast. Jeśli na przykład rozpoczniemy przesyłanie, musimy zatrzymać odtwarzanie lokalne 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 to sposób, w jaki platforma Cast wysyła i odbiera wiadomości z 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 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 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ć 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)
        }
    }
    ...
}

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 utworzy automatycznie instancję RemoteMediaClient. Można uzyskać do niego dostęp, wywołując metodę getRemoteMediaClient() w instancji CastSession. Dodaj do LocalPlayerActivity te metody, aby wczytać wybrany film do odbiornika:

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 Uruchom w Android Studio z zielonym trójkątem wskazującym 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 projektowania przesyłania wymaga, by cała aplikacja Cast udostępniała minikontroler, który pojawia się, gdy użytkownik opuści bieżącą stronę z treściami. Minikontroler zapewnia natychmiastowy dostęp i widoczne przypomnienie o bieżącej sesji przesyłania.

Grafika przedstawiająca dolną część telefonu z Androidem, na której widać miniodtwarzacz w aplikacji Przesyłanie filmów.

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 Uruchom w Android Studio z zielonym trójkątem wskazującym w prawoUruchom, aby uruchomić aplikację i przesłać film. Po rozpoczęciu odtwarzania na odbiorniku u dołu każdego działania powinien pojawić się minikontroler. Możesz sterować zdalnym odtwarzaniem 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 podczas projektowania wymaga, aby aplikacja nadawcy mogła obsługiwać multimedia z poziomu powiadomienia i ekranu blokady.

Ilustracja telefonu z Androidem, na którym widać opcje 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. Elementy sterujące multimediami w powiadomieniach i na ekranie blokady są domyślnie włączone. Blokada ekranu 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()
}

Kliknij przycisk Przycisk Uruchom w Android Studio z zielonym trójkątem wskazującym w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym. Prześlij film i zamknij aplikację przykładową. Na odbiorniku powinno wyświetlić się powiadomienie o filmie, który jest właśnie odtwarzany. Zablokuj urządzenie mobilne, a na ekranie blokady powinny pojawić się elementy sterujące odtwarzaniem multimediów na urządzeniu przesyłającym.

Grafika przedstawiająca telefon z Androidem, na którym wyświetlane są opcje sterowania multimediami na ekranie blokady.

9. Nakładka wprowadzająca

Lista kontrolna dotycząca projektowania w Google Cast wymaga, by aplikacja nadawcy zaprezentowała przycisk przesyłania dotychczasowym użytkownikom. Dzięki temu dowiedzą się, że obsługuje ona przesyłanie, a także pomaga użytkownikom, którzy dopiero zaczynają korzystać z Google Cast.

Ilustracja przedstawiająca wprowadzającą nakładkę przesyłania wokół przycisku Cast w aplikacji Cast 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 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 pojawia, 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 pełnoekranowa wersja minikontrolera.

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

Pakiet Cast SDK zawiera widżet ExpandedControllerActivity dla rozwiniętego kontrolera. 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 w rozwiniętym kontrolerze udostępnić 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 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 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 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 Uruchom w Android Studio z zielonym trójkątem wskazującym w prawoUruchom, aby uruchomić aplikację na urządzeniu mobilnym i przesłać film. Zobaczysz rozwinięty kontroler. Wróć do listy filmów, a gdy klikniesz minikontroler, rozwinięty kontroler zostanie ponownie załadowany. Aby zobaczyć powiadomienie, zamknij 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 można było zaimplementować Cast Connect, play-services-cast-framework musi mieć co najmniej 19.0.0.

LaunchOptions

Aby uruchomić aplikację na Androida TV (znaną też jako odbiornik Androida), w obiekcie LaunchOptions trzeba ustawić flagę setAndroidReceiverCompatible na „true”. 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()

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. Identyfikator CredentialsData jest przekazywany do aplikacji na Androida TV tylko podczas uruchamiania lub dołączania do niej. Jeśli ustawisz je ponownie po nawiązaniu połączenia, nie zostanie ono przekazane do aplikacji na Androida TV.

Aby można było ustawić dane logowania do uruchamiania, należy zdefiniować pole CredentialsData i przekazać je 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()

Ustaw dane logowania dla żądania LoadRequest

Jeśli aplikacja odbiornika internetowego i aplikacja na Androida TV obsługuje funkcje credentials w inny sposób, konieczne może być zdefiniowanie dla każdego z nich osobnego parametru credentials. 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 odbiornika, do której nadawca przesyła treści, pakiet SDK będzie teraz automatycznie obsługiwać dane uwierzytelniające, których chcesz 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 ta opcja jest 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 z tego folderu w Androidzie TV, uruchamiając polecenie:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Aplikacja Przesyłaj filmy powinna być teraz widoczna w menu Twoje aplikacje na urządzeniu z Androidem TV.
  2. Wróć do projektu Android Studio i kliknij przycisk Uruchom, aby zainstalować uruchomić aplikację nadawcy na fizycznym urządzeniu mobilnym, W prawym górnym rogu kliknij ikonę przesyłania i z dostępnych opcji wybierz urządzenie z Androidem TV. 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

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 programistów Android Sender.