Membuat aplikasi Android kompatibel untuk Cast

1. Ringkasan

Logo Google Cast

Codelab ini akan mengajarkan cara memodifikasi aplikasi video Android yang ada untuk mentransmisikan konten di perangkat yang kompatibel dengan Google Cast.

Apa itu Google Cast?

Google Cast memungkinkan pengguna mentransmisikan konten dari perangkat seluler ke TV. Pengguna kemudian dapat menggunakan perangkat seluler sebagai remote control untuk pemutaran media di TV.

Google Cast SDK memungkinkan Anda memperluas aplikasi untuk mengontrol TV atau sistem suara. Cast SDK memungkinkan Anda menambahkan komponen UI yang diperlukan berdasarkan Checklist Desain Google Cast.

Checklist Desain Google Cast disediakan untuk menyederhanakan pengalaman pengguna Cast dan membuatnya dapat diprediksi di semua platform yang didukung.

Apa yang akan kita buat?

Setelah menyelesaikan codelab ini, Anda akan memiliki aplikasi video Android yang dapat mentransmisikan video ke perangkat yang kompatibel untuk Google Cast.

Yang akan Anda pelajari

  • Cara menambahkan Google Cast SDK ke aplikasi video sampel.
  • Cara menambahkan tombol Cast untuk memilih perangkat Google Cast.
  • Cara menghubungkan ke perangkat Cast dan meluncurkan penerima media.
  • Cara mentransmisikan video.
  • Cara menambahkan pengontrol mini Cast ke aplikasi Anda.
  • Cara mendukung notifikasi media dan kontrol layar kunci.
  • Cara menambahkan pengontrol yang diperluas.
  • Cara menyediakan overlay perkenalan.
  • Cara menyesuaikan widget Cast.
  • Cara berintegrasi dengan Cast Connect

Yang Anda butuhkan

  • Android SDK terbaru.
  • Android Studio versi 3.2+
  • Satu perangkat seluler dengan Android 4.1+ Jelly Bean (API level 16).
  • Kabel data USB untuk menyambungkan perangkat seluler ke komputer pengembangan.
  • Perangkat Google Cast seperti Chromecast atau Android TV yang dikonfigurasi dengan akses internet.
  • TV atau monitor dengan input HDMI.
  • Chromecast dengan Google TV diperlukan untuk menguji integrasi Cast Connect, tetapi bersifat opsional untuk bagian Codelab lainnya. Jika Anda tidak memilikinya, jangan ragu untuk melewati langkah Tambahkan Dukungan Cast Connect, di akhir tutorial ini.

Pengalaman

  • Sebelumnya, Anda harus memiliki pengetahuan pengembangan Kotlin dan Android.
  • Anda juga perlu memiliki pengetahuan tentang menonton TV :)

Bagaimana Anda akan menggunakan tutorial ini?

Hanya membacanya Membacanya dan menyelesaikan latihan

Bagaimana Anda menilai pengalaman membuat aplikasi Android Anda?

Pemula Menengah Mahir

Bagaimana Anda menilai pengalaman menonton TV Anda?

Pemula Menengah Mahir

2. Mendapatkan kode contoh

Anda dapat mendownload semua kode contoh ke komputer Anda...

dan mengekstrak file zip yang didownload.

3. Menjalankan aplikasi contoh

ikon sepasang kompas

Pertama, mari kita lihat bagaimana tampilan contoh aplikasi yang sudah selesai. Aplikasi ini adalah pemutar video dasar. Pengguna dapat memilih video dari daftar dan kemudian dapat memutar video secara lokal di perangkat atau mentransmisikannya ke perangkat Google Cast.

Setelah mendownload kode, petunjuk berikut menjelaskan cara membuka dan menjalankan contoh aplikasi yang telah selesai di Android Studio:

Pilih Import Project di layar sambutan atau opsi menu File > New > Import Project....

Pilih direktori ikon folderapp-done dari folder kode contoh dan klik OK.

Klik File > Tombol 'Sync Project with Gradle' Android Studio Sync Project with Gradle Files.

Aktifkan proses debug USB di perangkat Android Anda – di Android 4.2 dan versi yang lebih baru, layar Opsi developer disembunyikan secara default. Agar terlihat, buka Setelan > Tentang ponsel lalu ketuk Nomor versi tujuh kali. Kembali ke layar sebelumnya, buka Sistem > Lanjutan dan ketuk Opsi developer di dekat bagian bawah, lalu ketuk Proses debug USB untuk mengaktifkannya.

Colokkan perangkat Android Anda dan klik tombol Tombol Run Android Studio, segitiga hijau yang menunjuk ke kananRun di Android Studio. Anda akan melihat aplikasi video bernama Cast Videos muncul setelah beberapa detik.

Klik tombol Cast di aplikasi video dan pilih perangkat Google Cast.

Pilih video dan klik tombol putar.

Video akan mulai diputar di perangkat Google Cast.

Pengontrol yang diperluas akan ditampilkan. Anda dapat menggunakan tombol putar/jeda untuk mengontrol pemutaran.

Kembali ke daftar video.

Pengontrol mini kini terlihat di bagian bawah layar. Ilustrasi ponsel Android yang menjalankan aplikasi 'Transmisikan Video' dengan pengontrol mini yang muncul di bagian bawah layar

Klik tombol jeda di pengontrol mini untuk menjeda video pada penerima. Klik tombol putar di pengontrol mini untuk melanjutkan pemutaran video lagi.

Klik tombol layar utama perangkat seluler. Tarik turun notifikasi dan Anda akan melihat notifikasi untuk sesi Cast.

Kunci ponsel dan saat membuka kunci, Anda akan melihat notifikasi di layar kunci untuk mengontrol pemutaran media atau menghentikan transmisi.

Kembali ke aplikasi video dan klik tombol Cast untuk menghentikan transmisi di perangkat Google Cast.

Pertanyaan umum (FAQ)

4. Menyiapkan project awal

Ilustrasi ponsel Android yang menjalankan aplikasi 'Cast Video'

Kita perlu menambahkan dukungan untuk Google Cast ke aplikasi awal yang Anda download. Berikut adalah beberapa istilah Google Cast yang akan kita gunakan dalam codelab ini:

  • aplikasi pengirim berjalan di perangkat seluler atau laptop,
  • aplikasi penerima berjalan di perangkat Google Cast.

Sekarang Anda siap untuk mengerjakan project awal menggunakan Android Studio:

  1. Pilih direktori ikon folderapp-start dari kode contoh yang Anda download (Pilih Import Project di layar sambutan atau opsi menu File > New > Import Project...).
  2. Klik tombol Tombol 'Sync Project with Gradle' Android Studio Sync Project with Gradle Files.
  3. Klik tombol Tombol Run Android Studio, segitiga hijau yang mengarah ke kananRun untuk menjalankan aplikasi dan menjelajahi UI.

Desain aplikasi

Aplikasi ini mengambil daftar video dari server web jarak jauh dan menyediakan daftar untuk dijelajahi pengguna. Pengguna dapat memilih video untuk melihat detailnya atau memutar video secara lokal di perangkat seluler.

Aplikasi ini terdiri dari dua aktivitas utama: VideoBrowserActivity dan LocalPlayerActivity. Untuk mengintegrasikan fungsi Google Cast, Aktivitas perlu mewarisi dari AppCompatActivity atau induknya, FragmentActivity. Batasan ini ada karena kita perlu menambahkan MediaRouteButton (disediakan di support library MediaRouter) sebagai MediaRouteActionProvider dan ini hanya akan berfungsi jika aktivitas mewarisi dari class yang disebutkan di atas. Support library MediaRouter bergantung pada support library AppCompat yang menyediakan class yang diperlukan.

VideoBrowserActivity

Aktivitas ini berisi Fragment (VideoBrowserFragment). Daftar ini didukung oleh ArrayAdapter (VideoListAdapter). Daftar video dan metadata terkaitnya dihosting di server jarak jauh sebagai file JSON. AsyncTaskLoader (VideoItemLoader) mengambil JSON ini dan memprosesnya untuk membuat daftar objek MediaItem.

Objek MediaItem memodelkan video dan metadatanya yang terkait, seperti judul, deskripsi, URL untuk streaming, URL untuk gambar pendukung, dan Trek Teks terkait (untuk teks tertutup) jika ada. Objek MediaItem diteruskan di antara aktivitas, sehingga MediaItem memiliki metode utilitas untuk mengonversinya menjadi Bundle dan sebaliknya.

Saat loader membuat daftar MediaItems, loader akan meneruskan daftar tersebut ke VideoListAdapter yang kemudian menyajikan daftar MediaItems dalam VideoBrowserFragment. Pengguna akan diberikan daftar thumbnail video dengan deskripsi singkat untuk setiap video. Saat item dipilih, MediaItem yang sesuai akan dikonversi menjadi Bundle dan diteruskan ke LocalPlayerActivity.

LocalPlayerActivity

Aktivitas ini menampilkan metadata tentang video tertentu dan memungkinkan pengguna memutar video secara lokal di perangkat seluler.

Aktivitas ini menghosting VideoView, beberapa kontrol media, dan area teks untuk menampilkan deskripsi video yang dipilih. Pemutar menutupi bagian atas layar, dengan menyisakan ruang untuk deskripsi mendetail video di bawahnya. Pengguna dapat memutar/menjeda atau mencari pemutaran lokal video.

Dependensi

Karena menggunakan AppCompatActivity, kita memerlukan support library AppCompat. Untuk mengelola daftar video dan mendapatkan gambar secara asinkron untuk daftar, kita menggunakan library Volley.

Pertanyaan umum (FAQ)

5. Menambahkan tombol Cast

Ilustrasi bagian atas ponsel Android dengan aplikasi Transmisi Video yang berjalan; tombol Transmisi muncul di sudut kanan atas layar

Aplikasi yang kompatibel untuk Cast menampilkan tombol Cast di setiap aktivitasnya. Mengklik tombol Cast akan menampilkan daftar perangkat Cast yang dapat dipilih pengguna. Jika pengguna memutar konten secara lokal di perangkat pengirim, memilih perangkat Cast akan memulai atau melanjutkan pemutaran di perangkat Cast tersebut. Kapan saja selama sesi Cast, pengguna dapat mengklik tombol Cast dan menghentikan transmisi aplikasi ke perangkat Cast. Pengguna harus dapat menghubungkan ke atau memutuskan sambungan dari perangkat Cast saat dalam aktivitas aplikasi Anda, seperti yang dijelaskan dalam Checklist Desain Google Cast.

Dependensi

Perbarui file build.gradle aplikasi untuk menyertakan dependensi library yang diperlukan:

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

Sinkronkan project untuk mengonfirmasikan project dibuat tanpa error.

Inisialisasi

Framework Cast memiliki objek singleton global, CastContext, yang mengoordinasikan semua interaksi Cast.

Anda harus menerapkan antarmuka OptionsProvider untuk menyediakan CastOptions yang diperlukan untuk menginisialisasi singleton CastContext. Opsi yang paling penting adalah ID aplikasi penerima, yang digunakan untuk memfilter hasil penemuan perangkat Cast dan meluncurkan aplikasi penerima saat sesi Cast dimulai.

Saat mengembangkan aplikasi yang kompatibel untuk Cast, Anda harus mendaftar sebagai developer Cast, lalu mendapatkan ID aplikasi untuk aplikasi tersebut. Untuk codelab ini, kita akan menggunakan contoh ID aplikasi.

Tambahkan file CastOptionsProvider.kt baru berikut ke paket com.google.sample.cast.refplayer project:

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
    }
}

Sekarang deklarasikan OptionsProvider dalam tag "application" dari file AndroidManifest.xml aplikasi:

<meta-data
    android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
    android:value="com.google.sample.cast.refplayer.CastOptionsProvider" />

Menginisialisasi dengan mudah CastContext dalam metode 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)
}

Tambahkan logika inisialisasi yang sama ke LocalPlayerActivity.

Tombol Cast

Setelah CastContext diinisialisasi, kita perlu menambahkan tombol Cast untuk memungkinkan pengguna memilih perangkat Cast. Tombol Transmisi diimplementasikan oleh MediaRouteButton dari support library MediaRouter. Seperti ikon tindakan apa pun yang dapat ditambahkan ke aktivitas (menggunakan ActionBar atau Toolbar), Anda harus menambahkan item menu yang sesuai ke menu terlebih dahulu.

Edit file res/menu/browse.xml dan tambahkan item MediaRouteActionProvider di menu sebelum item setelan:

<item
    android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
    app:showAsAction="always"/>

Ganti metode onCreateOptionsMenu() VideoBrowserActivity dengan menggunakan CastButtonFactory untuk menghubungkan MediaRouteButton ke framework 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
}

Ganti onCreateOptionsMenu di LocalPlayerActivity dengan cara yang sama.

Klik tombol Tombol Run Android Studio, segitiga hijau yang mengarah ke kananRun untuk menjalankan aplikasi di perangkat seluler Anda. Anda akan melihat tombol Cast di panel tindakan aplikasi dan saat diklik, tombol Cast akan mencantumkan perangkat Cast di jaringan lokal. Penemuan perangkat dikelola secara otomatis oleh CastContext. Pilih perangkat Cast dan contoh aplikasi penerima yang akan dimuat di perangkat Cast. Anda dapat menavigasi antara aktivitas penjelajahan dan aktivitas pemutar lokal, dan status tombol Cast akan tetap sinkron.

Kami belum menyediakan dukungan untuk pemutaran media, jadi Anda belum dapat memutar video di perangkat Cast. Klik tombol Cast untuk memutuskan sambungan.

6. Mentransmisikan konten video

Ilustrasi ponsel Android yang menjalankan aplikasi &#39;Cast Video&#39;

Kami akan memperluas contoh aplikasi untuk memutar video dari jarak jauh di perangkat Cast juga. Untuk melakukannya, kita harus memantau berbagai peristiwa yang dihasilkan oleh framework Cast.

Mentransmisikan media

Pada level tinggi, jika ingin memutar media di perangkat Cast, Anda perlu melakukan hal berikut:

  1. Buat objek MediaInfo yang memodelkan item media.
  2. Hubungkan ke perangkat Cast dan luncurkan aplikasi penerima.
  3. Muat objek MediaInfo ke penerima Anda dan putar kontennya.
  4. Lacak status media.
  5. Kirim perintah pemutaran ke penerima berdasarkan interaksi pengguna.

Kita telah menyelesaikan Langkah 2 di bagian sebelumnya. Langkah 3 mudah dilakukan dengan framework Cast. Langkah 1 berarti memetakan satu objek ke objek lainnya; MediaInfo adalah sesuatu yang dipahami oleh framework Cast dan MediaItem adalah enkapsulasi aplikasi kita untuk item media; kita dapat memetakan MediaItem ke MediaInfo dengan mudah.

Aplikasi contoh LocalPlayerActivity sudah membedakan antara pemutaran lokal vs jarak jauh dengan menggunakan enum ini:

private var mLocation: PlaybackLocation? = null

enum class PlaybackLocation {
    LOCAL, REMOTE
}

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

Dalam codelab ini Anda tidak perlu memahami keseluruhan cara kerja semua logika pemutar contoh. Tetapi Anda perlu memahami bahwa pemutar media aplikasi harus dimodifikasi agar mengetahui dua lokasi pemutaran dengan cara yang serupa.

Saat ini pemutar lokal selalu dalam status pemutaran lokal karena belum mengetahui apa pun tentang status Transmisi. Kita perlu mengupdate UI berdasarkan transisi status yang terjadi dalam framework Cast. Misalnya, jika mulai melakukan transmisi, kita perlu menghentikan pemutaran lokal dan menonaktifkan beberapa kontrol. Demikian pula, jika menghentikan transmisi saat berada dalam aktivitas ini, kita perlu beralih ke pemutaran lokal. Untuk menanganinya, kita perlu memantau berbagai peristiwa yang dihasilkan oleh framework Cast.

Pengelolaan sesi transmisi

Untuk framework Cast, sesi Cast menggabungkan langkah-langkah untuk menghubungkan ke perangkat, meluncurkan (atau bergabung), menghubungkan ke aplikasi penerima, dan menginisialisasi saluran kontrol media jika sesuai. Saluran kontrol media adalah cara framework Cast mengirim dan menerima pesan dari pemutar media penerima.

Sesi Transmisi akan dimulai secara otomatis saat pengguna memilih perangkat dari tombol Cast, dan akan dihentikan secara otomatis saat pengguna memutuskan sambungan. Menyambungkan kembali ke sesi penerima karena masalah jaringan juga ditangani secara otomatis oleh SDK Cast.

Mari tambahkan SessionManagerListener ke 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()
       }
   }
}

Dalam aktivitas LocalPlayerActivity, kita ingin diberi tahu saat tersambung atau terputus dari perangkat Cast sehingga kita dapat beralih ke atau dari pemutar lokal. Perhatikan, konektivitas dapat terganggu bukan hanya oleh instance aplikasi Anda yang berjalan pada perangkat seluler, tetapi juga bisa terganggu oleh instance aplikasi lainnya (atau hal lainnya) yang berjalan pada perangkat seluler berbeda.

Sesi yang saat ini aktif dapat diakses sebagai SessionManager.getCurrentSession(). Sesi dibuat dan dihapus secara otomatis sebagai respons terhadap interaksi pengguna dengan dialog Cast.

Kita harus mendaftarkan pemroses sesi dan menginisialisasi beberapa variabel yang akan kita gunakan dalam aktivitas. Ubah metode LocalPlayerActivity onCreate menjadi:

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)
        }
    }
    ...
}

Memuat media

Di SDK Cast, RemoteMediaClient menyediakan kumpulan API yang mudah digunakan untuk mengelola pemutaran media jarak jauh pada penerima. Untuk CastSession yang mendukung pemutaran media, instance RemoteMediaClient akan dibuat secara otomatis oleh SDK. Ini dapat diakses dengan memanggil metode getRemoteMediaClient() pada instance CastSession. Tambahkan metode berikut ke LocalPlayerActivity untuk memuat video yang saat ini dipilih di penerima:

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()
    }
}

Sekarang perbarui berbagai metode yang ada untuk menggunakan logika sesi Cast guna mendukung pemutaran jarak jauh:

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()
}

Untuk metode updatePlayButton, ubah nilai variabel isConnected:

private fun updatePlayButton(state: PlaybackState?) {
    ...
    val isConnected = (mCastSession != null
                && (mCastSession!!.isConnected || mCastSession!!.isConnecting))
    ...
}

Sekarang, klik tombol Tombol Run Android Studio, segitiga hijau yang mengarah ke kananRun untuk menjalankan aplikasi di perangkat seluler Anda. Sambungkan ke perangkat Cast dan mulai putar video. Anda akan melihat video yang diputar pada penerima.

7. Pengontrol mini

Checklist Desain Cast mengharuskan semua aplikasi Cast menyediakan pengontrol mini yang muncul saat pengguna keluar dari halaman konten saat ini. Pengontrol mini menyediakan akses instan dan pengingat yang terlihat untuk sesi Cast saat ini.

Ilustrasi bagian bawah ponsel Android yang menampilkan miniplayer di aplikasi Cast Video

SDK Cast menyediakan tampilan kustom, MiniControllerFragment, yang dapat ditambahkan ke file tata letak aplikasi dari aktivitas tempat Anda ingin menampilkan pengontrol mini.

Tambahkan definisi fragmen berikut di bagian bawah res/layout/player_activity.xml dan res/layout/video_browser.xml:

<fragment
    android:id="@+id/castMiniController"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:visibility="gone"
    class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment"/>

Klik tombol Tombol Run Android Studio, segitiga hijau yang mengarah ke kananRun untuk menjalankan aplikasi dan mentransmisikan video. Saat pemutaran dimulai pada penerima, Anda akan melihat pengontrol mini muncul di bagian bawah setiap aktivitas. Anda dapat mengontrol pemutaran jarak jauh menggunakan pengontrol mini. Jika Anda bernavigasi antara aktivitas penjelajahan dan aktivitas pemutar lokal, status pengontrol mini harus tetap sinkron dengan status pemutaran media penerima.

8. Notifikasi dan layar kunci

Checklist desain Google Cast memerlukan aplikasi pengirim untuk menerapkan kontrol media dari notifikasi dan layar kunci.

Ilustrasi ponsel Android yang menampilkan kontrol media di area notifikasi

SDK Cast menyediakan MediaNotificationService untuk membantu aplikasi pengirim membuat kontrol media untuk notifikasi dan layar kunci. Layanan secara otomatis akan digabungkan ke dalam manifes aplikasi Anda berdasarkan gradle.

MediaNotificationService akan berjalan di latar belakang saat pengirim melakukan transmisi, dan akan menampilkan notifikasi dengan thumbnail gambar dan metadata tentang item transmisi saat ini, tombol putar/jeda, dan tombol berhenti.

Kontrol notifikasi dan layar kunci dapat diaktifkan dengan CastOptions saat menginisialisasi CastContext. Kontrol media untuk notifikasi dan layar kunci diaktifkan secara default. Fitur layar kunci diaktifkan selama notifikasi diaktifkan.

Edit CastOptionsProvider dan ubah implementasi getCastOptions agar cocok dengan kode ini:

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()
}

Klik tombol Tombol Run Android Studio, segitiga hijau yang mengarah ke kananRun untuk menjalankan aplikasi di perangkat seluler Anda. Transmisikan video dan keluar dari aplikasi contoh. Harus ada notifikasi untuk video yang sedang diputar di penerima. Kunci perangkat seluler Anda dan layar kunci akan menampilkan kontrol untuk pemutaran media di perangkat Cast.

Ilustrasi ponsel Android yang menampilkan kontrol media di layar kunci

9. Overlay perkenalan

Checklist desain Google Cast memerlukan aplikasi pengirim untuk memperkenalkan tombol Cast kepada pengguna yang ada guna memberi tahu bahwa aplikasi pengirim kini mendukung transmisi dan juga membantu pengguna yang baru menggunakan Google Cast.

Ilustrasi yang menampilkan overlay Cast pengantar di sekitar tombol Cast di aplikasi Android Cast Video

SDK Cast menyediakan tampilan kustom, IntroductoryOverlay, yang dapat digunakan untuk menyorot tombol Cast saat pertama kali ditampilkan kepada pengguna. Tambahkan kode berikut ke VideoBrowserActivity:

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()
        }
    }
}

Sekarang, tambahkan CastStateListener dan panggil metode showIntroductoryOverlay saat perangkat Cast tersedia dengan mengubah metode onCreate dan mengganti metode onResume dan onPause agar cocok dengan kode berikut:

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!!)
}

Hapus data aplikasi atau hapus aplikasi dari perangkat Anda. Kemudian, klik tombol Tombol Run Android Studio, segitiga hijau yang mengarah ke kananRun untuk menjalankan aplikasi di perangkat seluler dan Anda akan melihat overlay perkenalan (hapus data aplikasi jika overlay tidak ditampilkan).

10. Pengontrol yang diperluas

Checklist desain Google Cast mengharuskan aplikasi pengirim untuk menyediakan pengontrol yang diperluas untuk media yang ditransmisi. Pengontrol yang diperluas adalah versi layar penuh dari pengontrol mini.

Ilustrasi video yang diputar di ponsel Android dengan pengontrol yang diperluas yang ditempatkan di atasnya

SDK Cast menyediakan widget untuk pengontrol yang diperluas yang disebut ExpandedControllerActivity. Ini adalah class abstrak yang harus dibuat subclass untuk menambahkan tombol Cast.

Pertama, buat file resource menu baru, yang disebut expanded_controller.xml, agar pengontrol yang diperluas dapat menyediakan tombol 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>

Buat paket baru expandedcontrols dalam paket com.google.sample.cast.refplayer. Selanjutnya, buat file baru bernama ExpandedControlsActivity.kt dalam paket com.google.sample.cast.refplayer.expandedcontrols.

package com.google.sample.cast.refplayer.expandedcontrols

import android.view.Menu
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
import com.google.sample.cast.refplayer.R
import com.google.android.gms.cast.framework.CastButtonFactory

class ExpandedControlsActivity : ExpandedControllerActivity() {
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        super.onCreateOptionsMenu(menu)
        menuInflater.inflate(R.menu.expanded_controller, menu)
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
        return true
    }
}

Sekarang, deklarasikan ExpandedControlsActivity di AndroidManifest.xml dalam tag application di atas 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>

Edit CastOptionsProvider dan ubah NotificationOptions dan CastMediaOptions untuk menetapkan aktivitas target ke 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()
}

Perbarui metode LocalPlayerActivity loadRemoteMedia untuk menampilkan ExpandedControlsActivity saat media jarak jauh dimuat:

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())
}

Klik tombol Tombol Run Android Studio, segitiga hijau yang menunjuk ke kananRun untuk menjalankan aplikasi di perangkat seluler dan mentransmisikan video. Anda akan melihat pengontrol yang diperluas. Kembali ke daftar video dan saat Anda mengklik pengontrol mini, pengontrol yang diperluas akan dimuat lagi. Keluar dari aplikasi untuk melihat notifikasi. Klik gambar notifikasi untuk memuat pengontrol yang diperluas.

11. Menambahkan dukungan Cast Connect

Library Cast Connect memungkinkan aplikasi pengirim yang ada untuk berkomunikasi dengan aplikasi Android TV melalui protokol Cast. Cast Connect dibuat di atas infrastruktur Cast, dengan aplikasi Android TV yang bertindak sebagai penerima.

Dependensi

Catatan: Untuk menerapkan Cast Connect, play-services-cast-framework harus 19.0.0 atau yang lebih tinggi.

LaunchOptions

Untuk meluncurkan aplikasi Android TV, yang juga disebut sebagai Penerima Android, kita perlu menetapkan tanda setAndroidReceiverCompatible ke benar di objek LaunchOptions. Objek LaunchOptions ini menentukan cara penerima diluncurkan dan diteruskan ke CastOptions yang ditampilkan oleh class CastOptionsProvider. Menetapkan tanda yang disebutkan di atas ke false akan meluncurkan penerima web untuk ID Aplikasi yang ditentukan di Konsol Developer Cast.

Di file CastOptionsProvider.kt, tambahkan kode berikut ke metode getCastOptions:

import com.google.android.gms.cast.LaunchOptions
...
val launchOptions = LaunchOptions.Builder()
            .setAndroidReceiverCompatible(true)
            .build()
return new CastOptions.Builder()
        .setLaunchOptions(launchOptions)
        ...
        .build()

Menetapkan Kredensial Peluncuran

Di sisi pengirim, Anda dapat menentukan CredentialsData untuk mewakili siapa yang bergabung ke sesi. credentials adalah string yang dapat ditentukan pengguna, selama aplikasi ATV Anda dapat memahaminya. CredentialsData hanya diteruskan ke aplikasi Android TV Anda selama waktu peluncuran atau bergabung. Jika Anda menyetelnya lagi saat terhubung, setelan tersebut tidak akan diteruskan ke aplikasi Android TV Anda.

Untuk menetapkan Kredensial Peluncuran, CredentialsData harus ditentukan dan diteruskan ke objek LaunchOptions. Tambahkan kode berikut ke metode getCastOptions di file CastOptionsProvider.kt Anda:

import com.google.android.gms.cast.CredentialsData
...

val credentialsData = CredentialsData.Builder()
        .setCredentials("{\"userId\": \"abc\"}")
        .build()
val launchOptions = LaunchOptions.Builder()
       ...
       .setCredentialsData(credentialsData)
       .build()

Menetapkan Kredensial saat LoadRequest

Jika aplikasi Web Receiver dan aplikasi Android TV Anda menangani credentials secara berbeda, Anda mungkin perlu menentukan credentials terpisah untuk masing-masing. Untuk menanganinya, tambahkan kode berikut di file LocalPlayerActivity.kt Anda di bagian fungsi loadRemoteMedia:

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

Bergantung pada aplikasi penerima yang menjadi tujuan transmisi pengirim, SDK kini akan otomatis menangani kredensial mana yang akan digunakan untuk sesi saat ini.

Menguji Cast Connect

Langkah-langkah untuk menginstal APK Android TV di Chromecast dengan Google TV

  1. Temukan Alamat IP perangkat Android TV Anda. Biasanya, opsi ini tersedia di bagian Setelan > Jaringan & Internet > (Nama jaringan yang terhubung ke perangkat Anda). Di sebelah kanan, Anda akan melihat detail dan IP perangkat Anda di jaringan.
  2. Gunakan alamat IP untuk perangkat Anda agar terhubung melalui ADB menggunakan terminal:
$ adb connect <device_ip_address>:5555
  1. Dari jendela terminal, buka folder tingkat atas untuk mendapatkan contoh codelab yang Anda download di awal codelab ini. Contoh:
$ cd Desktop/android_codelab_src
  1. Instal file .apk di folder ini ke Android TV dengan menjalankan:
$ adb -s <device_ip_address>:5555 install android-tv-app.apk
  1. Sekarang Anda akan dapat melihat aplikasi bernama Transmisikan Video di menu Aplikasi Anda di perangkat Android TV.
  2. Kembali ke project Android Studio Anda, lalu klik tombol Run untuk menginstal &menjalankan aplikasi pengirim di perangkat seluler fisik Anda. Di sudut kanan atas, klik ikon transmisi dan pilih perangkat Android TV Anda dari opsi yang tersedia. Sekarang Anda akan melihat aplikasi Android TV diluncurkan di perangkat Android TV dan memutar video akan memungkinkan Anda mengontrol pemutaran video menggunakan remote Android TV.

12. Menyesuaikan widget Cast

Anda dapat menyesuaikan widget Transmisikan dengan menyetel warna, menyesuaikan gaya tombol, teks, dan tampilan thumbnail, serta dengan memilih jenis tombol yang akan ditampilkan.

Mengupdate 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>

Deklarasikan tema khusus berikut:

<!-- 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. Selamat

Kini Anda telah mengetahui cara agar aplikasi video kompatibel untuk Cast menggunakan widget SDK Cast di Android.

Untuk detail selengkapnya, lihat panduan developer Pengirim Android.