1. Omówienie
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?
Jak oceniasz tworzenie aplikacji na Androida?
Jak oceniasz oglądanie telewizji?
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
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 app-done
z folderu przykładowego kodu i kliknij OK.
Kliknij Plik > 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 Uruchom 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.
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
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:
- Wybierz katalog
app-start
z pobranego przykładowego kodu (na ekranie powitalnym kliknij Import Project (Importuj projekt) lub w menu Plik > Nowy > Importuj projekt...). - Kliknij przycisk Synchronizuj projekt z plikami Gradle.
- Kliknij przycisk Uruchom, 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ń: VideoBrowserActivity
i LocalPlayerActivity
. 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ę MediaItems
w VideoBrowserFragment
. Użytkownik widzi listę miniatur filmów z krótkim opisem każdego filmu. Po wybraniu elementu odpowiadający mu element MediaItem
jest przekształcany w element Bundle
i przekazywany do elementu LocalPlayerActivity
.
LocalPlayerActivity
To działanie wyświetla metadane konkretnego filmu i pozwala użytkownikowi odtworzyć go lokalnie na urządzeniu mobilnym.
Aktywność zawiera VideoView
, niektóre elementy sterujące multimediami oraz obszar tekstowy z opisem wybranego filmu. Odtwarzacz 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
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 Uruchom, 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
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:
- Utwórz obiekt
MediaInfo
, który modeluje element multimedialny. - Połącz się z urządzeniem przesyłającym i uruchom aplikację odbiornika.
- Wczytaj obiekt
MediaInfo
do odbiornika i odtwórz treści. - śledzić stan multimediów.
- 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 Uruchom, 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.
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 Uruchom, 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.
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 Uruchom, 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.
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.
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 onResume
i onPause
, 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 Uruchom, 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.
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 Uruchom, 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
- 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.
- Użyj adresu IP, by połączyć się z urządzeniem przez ADB:
$ adb connect <device_ip_address>:5555
- 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
- Zainstaluj plik .apk z tego folderu w Androidzie TV, uruchamiając polecenie:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- Na urządzeniu z Androidem TV w menu Twoje aplikacje powinieneś/powinnaś zobaczyć aplikację o nazwie Cast Videos.
- 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.