1. Omówienie
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?
Jak oceniasz swoje doświadczenia z tworzeniem aplikacji na Androida?
Jak oceniasz swoje wrażenia z oglądania 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 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 app-done
z folderu przykładowego kodu i kliknij OK.
Kliknij Plik > 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 Uruchom 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.
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
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:
- 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 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
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 Uruchom, 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
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:
- Utwórz obiekt
MediaInfo
modelujący 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. - Śledź stan multimediów.
- 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 Uruchom, 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.
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 Uruchom, 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.
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 Uruchom, 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.
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.
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 Uruchom, 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.
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 Uruchom, 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
- 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.
- Użyj adresu IP, by połączyć się z urządzeniem przez ADB:
$ adb connect <device_ip_address>:5555
- 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
- Zainstaluj plik .apk z tego folderu w Androidzie TV, uruchamiając polecenie:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
- Aplikacja Przesyłaj filmy powinna być teraz widoczna w menu Twoje aplikacje na urządzeniu z Androidem TV.
- 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.