Dalam codelab ini, Anda akan mempelajari cara membuat aplikasi yang menambahkan saluran dan program ke layar utama Android TV menggunakan library Kotlin dan AndroidX. Layar utama memiliki lebih banyak fitur dibandingkan dengan cakupan codelab ini. Baca dokumentasi ini untuk mempelajari semua fitur dan kemampuan layar utama.
Konsep
Layar utama Android TV, atau singkatnya layar utama, menyediakan UI yang menampilkan rekomendasi konten sebagai tabel saluran dan program. Setiap baris adalah saluran. Saluran berisi kartu untuk setiap program yang tersedia di saluran itu. Aplikasi Anda dapat menawarkan sejumlah saluran untuk ditambahkan pengguna ke layar utama. Pengguna biasanya harus memilih dan menyetujui setiap saluran sebelum saluran itu muncul di layar utama.
Setiap aplikasi memiliki opsi untuk membuat satu saluran default. Saluran default bersifat khusus karena otomatis muncul di layar utama; pengguna tidak harus memintanya secara eksplisit.
Ringkasan
Codelab ini menunjukkan cara membuat, menambahkan, serta mengupdate saluran dan program di layar utama. Codelab ini menggunakan database tiruan dari koleksi dan film. Untuk mempermudah, daftar film yang sama digunakan untuk semua langganan.
Meng-clone repositori project awal
Codelab ini menggunakan Android Studio, sebuah IDE untuk mengembangkan aplikasi Android.
Jika Anda belum menginstalnya, download dan instal.
Anda dapat mendownload kode sumber dari repositori GitHub:
git clone https://github.com/googlecodelabs/tv-recommendations-kotlin.git
Atau Anda dapat mendownloadnya sebagai file zip.
Buka Android Studio dan klik File > Open dari panel menu atau Open an Existing Android Studio Project dari layar pembuka dan pilih folder yang baru saja di-clone.
Memahami project awal
Ada empat langkah dalam project. Pada setiap langkah, Anda akan menambahkan lebih banyak kode ke aplikasi, dan setelah menyelesaikan semua petunjuk di setiap bagian, Anda dapat membandingkan hasilnya dengan kode di langkah berikutnya.
Berikut adalah komponen utama dalam aplikasi:
MainActivity
adalah aktivitas entri project.model/TvMediaBackground
adalah objek untuk gambar latar saat menjelajahi film.model
/TvMediaCollection
adalah objek untuk koleksi film.model/TvMediaMetadata
adalah objek untuk menyimpan info film.model/TvMediaDatabase
adalah penampung database dan berfungsi sebagai titik akses utama bagi data film yang mendasarinya.fragments/NowPlayingFragment
memutar film.fragments
/MediaBrowserFragment
adalah fragmen browser media.workers/TvMediaSynchronizer
adalah class sinkronisasi data yang berisi kode untuk mengambil feed, membuat objek, dan mengupdate saluran.utils/TvLauncherUtils
adalah class helper untuk mengelola saluran dan program pratinjau menggunakan library AndroidX dan penyedia TV.
Menjalankan project awal
Coba jalankan project. Jika Anda mengalami masalah, lihat dokumentasi tentang cara memulai.
- Hubungkan Android TV atau mulai emulator.
- Pilih konfigurasi step_1, pilih perangkat Android Anda dan tekan tombol run di panel menu.
- Anda akan melihat outline aplikasi TV sederhana dengan tiga koleksi video.
Yang telah Anda pelajari
Dalam pengantar ini, Anda telah mempelajari tentang:
- Layar utama TV dan salurannya
- Struktur kode project dan class utama dalam codelab ini
Apa selanjutnya?
Menambahkan saluran ke layar utama
Mulai dengan menambahkan saluran ke layar utama. Setelah memiliki saluran, Anda dapat menyisipkan program ke dalam saluran. Pengguna dapat menemukan saluran Anda di panel konfigurasi saluran dan memilih saluran yang akan ditampilkan di UI beranda. Codelab ini membuat saluran untuk setiap koleksi media:
- Film Fitur Historis
- Film Fitur 1910-an
- Koleksi Charlie Chaplin
Bagian berikut menjelaskan cara memuat data dan menggunakannya untuk saluran.
Metode synchronize()
di TvMediaSynchronizer
akan melakukan tindakan berikut:
- Mengambil feed media, yang mencakup gambar latar, koleksi media, dan metadata video. Informasi ini ditentukan di
assets/media-feed.json
- Mengupdate instance
TvMediaDatabase
, yang menyimpan gambar latar, koleksi media, dan data video, ke masing-masing objeknya - Menggunakan
TvLauncherUtils
untuk membuat atau mengupdate saluran dan program
Jangan khawatir tentang pemuatan data dalam codelab ini. Tujuan codelab ini adalah memahami cara menggunakan library AndroidX untuk membuat saluran. Untuk melakukannya, Anda akan menambahkan kode ke beberapa metode di class TvLauncherUtils
.
Membuat saluran
Setelah mengambil data media dan menyimpannya ke database lokal, kode project akan mengonversi Collection
media ke saluran. Kode ini membuat dan mengupdate saluran dalam metode upsertChannel()
dari class TvLauncherUtils
.
- Buat instance
PreviewChannel.Builder()
. Untuk menghindari duplikasi saluran, codelab ini memeriksa keberadaan saluran dan hanya mengupdate-nya jika tersedia. Setiap koleksi video memiliki id terkait, dan Anda dapat menggunakannya sebagaiinternalProviderId
saluran. Untuk mengidentifikasi saluran yang ada, bandingkaninternalProviderId
dengan id koleksi. Salin dan tempel kode berikut ke dalamupsertChannel()
di komentar kode //TODO: Step 1 create or find an existing channel.
val channelBuilder = if (existingChannel == null) {
PreviewChannel.Builder()
} else {
PreviewChannel.Builder(existingChannel)
}
- Tetapkan atribut di
Builder
saluran (misalnya, nama dan logo/ikon saluran). Nama tampilan muncul di layar utama tepat di bawah ikon saluran. Android TV menggunakanappLinkIntentUri
untuk menavigasi pengguna saat mereka mengklik ikon saluran. Codelab ini menggunakan Uri ini untuk mengarahkan pengguna ke koleksi yang bersangkutan di aplikasi. Salin dan tempel kode berikut di komentar kode // TODO: Langkah 2 tambahkan metadata koleksi dan buat objek saluran.
val updatedChannel = channelBuilder
.setInternalProviderId(collection.id)
.setLogo(channelLogoUri)
.setAppLinkIntentUri(appUri)
.setDisplayName(collection.title)
.setDescription(collection.description)
.build()
- Panggil fungsi di class
PreviewChannelHelper
untuk menyisipkan saluran ke penyedia TV atau mengupdate-nya. Panggilan kepublishChannel()
menyisipkan nilai konten saluran ke penyedia TV.updatePreviewChannel
mengupdate saluran yang ada. Sisipkan kode berikut di komentar kode // TODO: Langkah 3.1 update saluran yang ada.
PreviewChannelHelper(context)
.updatePreviewChannel(existingChannel.id, updatedChannel)
Log.d(TAG, "Updated channel ${existingChannel.id}")
Sisipkan kode di bawah untuk membuat saluran baru di komentar kode // TODO: Langkah 3.2 publikasikan saluran.
val channelId = PreviewChannelHelper(context).publishChannel(updatedChannel)
Log.d(TAG, "Published channel $channelId")
channelId
- Tinjau metode
upsertChannel()
untuk melihat cara saluran dibuat atau diupdate.
Menjadikan saluran default terlihat
Saat Anda menambahkan saluran ke Penyedia TV, saluran tersebut tidak terlihat. Saluran tidak muncul di layar utama sampai pengguna memintanya. Pengguna biasanya harus memilih dan menyetujui setiap saluran sebelum saluran itu muncul di layar utama. Setiap aplikasi memiliki opsi untuk membuat satu saluran default. Saluran default bersifat khusus karena otomatis muncul di layar utama; pengguna tidak harus memintanya secara eksplisit.
Tambahkan kode berikut ke metode upsertChannel()
(di TODO: langkah 4 jadikan saluran default terlihat):
if(allChannels.none { it.isBrowsable }) {
TvContractCompat.requestChannelBrowsable(context, channelId)
}
Jika Anda memanggil requestChannelBrowsable()
untuk saluran non-default, dialog akan muncul untuk meminta izin pengguna.
Menjadwalkan update saluran
Setelah menambahkan kode pembuatan/update saluran, developer perlu memanggil metode synchronize()
untuk membuat saluran atau mengupdate-nya.
Saat terbaik untuk membuat saluran aplikasi Anda adalah langsung setelah pengguna menginstalnya. Anda dapat membuat penerima siaran mendengarkan pesan siaran android.media.tv.action.INITIALIZE_PROGRAMS
. Siaran ini akan dikirim setelah pengguna menginstal aplikasi TV, dan developer dapat melakukan beberapa inisialisasi program di sana.
Lihat file AndroidManifest.xml
dalam kode contoh dan temukan bagian penerima siaran. Coba temukan nama class yang benar untuk penerima siaran (akan dibahas berikutnya).
<action
android:name="android.media.tv.action.INITIALIZE_PROGRAMS" />
Buka class TvLauncherReceiver
dan lihat blok kode berikut untuk melihat bagaimana contoh aplikasi membuat saluran layar utama.
TvContractCompat.ACTION_INITIALIZE_PROGRAMS -> {
Log.d(TAG, "Handling INITIALIZE_PROGRAMS broadcast")
// Synchronizes all program and channel data
WorkManager.getInstance(context).enqueue(
OneTimeWorkRequestBuilder<TvMediaSynchronizer>().build())
}
Anda harus mengupdate saluran secara rutin. Codelab ini membuat tugas latar belakang menggunakan library WorkManager. Di class MainActivity
, TvMediaSynchronizer
digunakan untuk menjadwalkan update saluran reguler.
// Syncs the home screen channels hourly
// NOTE: It's very important to keep our content fresh in the user's home screen
WorkManager.getInstance(baseContext).enqueue(
PeriodicWorkRequestBuilder<TvMediaSynchronizer>(1, TimeUnit.HOURS)
.setInitialDelay(1, TimeUnit.HOURS)
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.build())
Menjalankan aplikasi
Jalankan aplikasi. Buka layar utama. Saluran default (My TV App Default) muncul, tetapi tidak memiliki program apa pun. Jika Anda menjalankan kode pada perangkat asli dan bukan emulator, saluran tersebut mungkin tidak muncul.
Menambahkan saluran lainnya
Feed berisi tiga koleksi. Di class TvMediaSynchronizer
, tambahkan saluran lain untuk koleksi ini (di TODO: langkah 5 tambahkan saluran lainnya).
feed.collections.subList(1, feed.collections.size).forEach {
TvLauncherUtils.upsertChannel(
context, it, database.metadata().findByCollection(it.id))
}
Menjalankan kembali aplikasi
Pastikan ketiga saluran telah dibuat. Klik tombol Costumize Channel, lalu TV Classics. Alihkan tombol sembunyikan/tampilkan di panel saluran untuk menyembunyikan/menampilkannya di layar utama.
Menghapus saluran
Jika aplikasi tidak lagi mempertahankan saluran, Anda dapat menghapusnya dari layar utama.
Telusuri langkah 6 dan temukan fungsi removeChannel
. Tambahkan bagian berikut ke dalamnya (di TODO: langkah 6 hapus saluran). Untuk melihat cara kerja kode ini, hapus koleksi dengan judul "Koleksi Charlie Chaplin" di media-feed.json
(pastikan menghapus seluruh koleksi). Jalankan kembali aplikasi, dan setelah beberapa detik, Anda akan melihat saluran tersebut telah dihapus.
// First, get all the channels added to the home screen
val allChannels = PreviewChannelHelper(context).allChannels
// Now find the channel with the matching content ID for our collection
val foundChannel = allChannels.find { it.internalProviderId == collection.id }
if (foundChannel == null) Log.e(TAG, "No channel with ID ${collection.id}")
// Use the found channel's ID to delete it from the content resolver
return foundChannel?.let {
PreviewChannelHelper(context).deletePreviewChannel(it.id)
Log.d(TAG, "Channel successfully removed from home screen")
// Remove all of the channel programs as well
val channelPrograms =
TvContractCompat.buildPreviewProgramsUriForChannel(it.id)
context.contentResolver.delete(channelPrograms, null, null)
// Return the ID of the channel removed
it.id
}
Setelah menyelesaikan semua petunjuk di atas, Anda dapat membandingkan kode aplikasi dengan step_2.
Yang telah Anda pelajari
- Cara membuat kueri untuk saluran.
- Cara menambahkan atau menghapus saluran dari layar utama.
- Cara menetapkan logo atau judul di saluran.
- Cara menjadikan saluran default terlihat.
- Cara menjadwalkan
WorkManager
untuk mengupdate saluran.
Apa selanjutnya?
Bagian berikutnya menunjukkan cara menambahkan program ke saluran.
Menambahkan program ke saluran mirip dengan membuat saluran. Gunakan PreviewProgram.Builder
, bukan PreviewChannel.Builder
.
Anda akan masih mengerjakan metode upsertChannel()
di class TvLauncherUtils
.
Membuat Program Pratinjau
Kita akan menambahkan kode ke step_2 di bagian berikut. Pastikan Anda membuat perubahan pada file sumber dalam modul tersebut di project Android Studio.
Setelah Anda tahu bahwa saluran terlihat, buat objek PreviewProgram
menggunakan objek Metadata
dengan PreviewProgram.Builder
. Sekali lagi, jangan menyisipkan program yang sama dua kali ke dalam saluran, sehingga contoh menetapkan metadata.id
ke contentId
dari PreviewProgram
untuk tujuan penghapusan duplikat. Tambahkan kode berikut di TODO: Langkah 7 buat atau temukan program pratinjau yang ada.
val existingProgram = existingProgramList.find { it.contentId == metadata.id }
val programBuilder = if (existingProgram == null) {
PreviewProgram.Builder()
} else {
PreviewProgram.Builder(existingProgram)
}
Buat builder dengan metadata media dan publikasikan/update di saluran. (TODO: Langkah 8 buat program pratinjau dan publikasikan.)
val updatedProgram = programBuilder.also { metadata.copyToBuilder(it) }
// Set the same channel ID in all programs
.setChannelId(channelId)
// This must match the desired intent filter in the manifest for VIEW action
.setIntentUri(Uri.parse("https://$host/program/${metadata.id}"))
// Build the program at once
.build()
Beberapa hal yang perlu diperhatikan di sini:
- Kode contoh menautkan metadata dengan program pratinjau melalui
contentId
. - Program Pratinjau disisipkan ke dalam saluran dengan memanggil
setChannelId()
diPreviewProgram.Builder()
. - Sistem Android TV meluncurkan
intentUri
program saat pengguna memilih sebuah program dari saluran.Uri
harus menyertakan ID program sehingga aplikasi dapat menemukan dan memutar media dari database saat pengguna memilih program.
Menambahkan program
Di sini, codelab ini menggunakan PreviewChannelHelper
dari library AndroidX untuk menyisipkan program ke dalam saluran.
Gunakan PreviewChannelHelper.publishPreviewProgram()
atau PreviewChannelHelper.updatePreviewProgram()
untuk menyimpan program di saluran (di TODO: Langkah 9 tambahkan program pratinjau ke saluran.)
try {
if (existingProgram == null) {
PreviewChannelHelper(context).publishPreviewProgram(updatedProgram)
Log.d(TAG, "Inserted program into channel: $updatedProgram")
} else {
PreviewChannelHelper(context)
.updatePreviewProgram(existingProgram.id, updatedProgram)
Log.d(TAG, "Updated program in channel: $updatedProgram")
}
} catch (exc: IllegalArgumentException) {
Log.e(TAG, "Unable to add program: $updatedProgram", exc)
}
Bagus! Aplikasi kini menambahkan program ke saluran. Anda dapat membandingkan kode dengan step_3.
Menjalankan aplikasi
Pilih step_2 dalam konfigurasi dan jalankan aplikasi.
Saat aplikasi berjalan, klik tombol Costumize Channel di bagian bawah layar utama dan cari aplikasi "TV Classics". Alihkan tiga saluran dan lihat log untuk mengetahui apa yang terjadi. Membuat saluran dan program terjadi di latar belakang. Jadi, jangan ragu untuk menambahkan laporan log ekstra untuk membantu Anda melacak peristiwa yang dipicu.
Yang telah Anda pelajari
- Cara menambahkan program ke saluran.
- Cara mengupdate atribut program.
Apa selanjutnya?
Menambahkan program ke Saluran Tonton Berikutnya.
Saluran Tonton Berikutnya berada di dekat bagian atas layar utama; muncul di bawah Aplikasi dan di atas semua saluran lainnya.
Konsep
Saluran Tonton Berikutnya memberikan cara bagi aplikasi Anda untuk mendorong interaksi dengan pengguna. Aplikasi Anda dapat menambahkan program berikut ke saluran Tonton Berikutnya: program yang ditandai menarik oleh pengguna, program yang dihentikan pengguna di tengah menonton, atau program yang terkait dengan konten yang ditonton pengguna (seperti episode berikutnya dalam seri atau season berikutnya dari sebuah acara). Ada 4 jenis kasus penggunaan untuk saluran Tonton Berikutnya:
- Melanjutkan menonton video yang belum selesai ditonton pengguna.
- Menyarankan video berikutnya untuk ditonton. Misalnya, jika pengguna selesai menonton episode 1, Anda dapat menyarankan episode 2.
- Menampilkan konten baru untuk mendorong interaksi.
- Mempertahankan daftar tontonan video menarik yang ditambahkan oleh pengguna.
Pelajaran ini menunjukkan cara menggunakan saluran Tonton Berikutnya untuk melanjutkan menonton video, khususnya cara menyertakan video di saluran Tonton Berikutnya saat pengguna menjedanya. Video harus dihapus dari saluran Tonton Berikutnya jika diputar hingga akhir.
Mengupdate posisi pemutaran
Ada beberapa cara untuk melacak posisi pemutaran konten yang dimainkan. Codelab ini menggunakan thread untuk menyimpan posisi pemutaran terbaru secara rutin ke dalam database dan untuk memperbarui metadata program Tonton Berikutnya. Buka step_3 dan ikuti petunjuk di bawah untuk menambahkan kode ini.
Di NowPlayingFragment
, tambahkan kode berikut dalam metode run()
dari updateMetadataTask.
(di TODO: langkah 10 update progres):
val contentDuration = player.duration
val contentPosition = player.currentPosition
// Updates metadata state
val metadata = args.metadata.apply {
playbackPositionMillis = contentPosition
}
Kode hanya menyimpan metadata jika posisi pemutaran kurang dari 95% dari total durasi.
Tambahkan kode berikut (di TODO: langkah 11 update metadata ke database) .
val programUri = TvLauncherUtils.upsertWatchNext(requireContext(), metadata)
lifecycleScope.launch(Dispatchers.IO) {
database.metadata().update(
metadata.apply { if (programUri != null) watchNext = true })
}
Jika posisi pemutaran telah melewati 95% dari video, program Tonton Berikutnya akan dihapus agar konten lain dapat diprioritaskan.
Di NowPlayingFragment
, tambahkan kode berikut untuk menghapus video yang sudah selesai dari baris Tonton Berikutnya (di TODO: langkah 12 hapus tonton berikutnya).
val programUri = TvLauncherUtils.removeFromWatchNext(requireContext(), metadata)
if (programUri != null) lifecycleScope.launch(Dispatchers.IO) {
database.metadata().update(metadata.apply { watchNext = false })
}
updateMetadataTask
dijadwalkan setiap 10 detik untuk memastikan posisi pemutaran terbaru dilacak. Ini dijadwalkan di onResume()
dan berhenti di onPause()
dari NowPlayingFragment
, sehingga data hanya akan diupdate saat pengguna menonton video.
Menambahkan/Mengupdate program Tonton Berikutnya
TvLauncherUtils
berinteraksi dengan Penyedia TV. Di langkah sebelumnya, removeFromWatchNext
dan upsertWatchNext
di TvLauncherUtils
dipanggil. Sekarang Anda perlu menerapkan kedua metode ini. Library AndroidX menyediakan class PreviewChannelHelper
yang menjadikan tugas ini sangat mudah.
Pertama, buat atau temukan instance WatchNextProgram.Builder
yang ada, lalu update objek dengan pemutaran terbaru metadata
. Tambahkan kode berikut dalam metode upsertWatchNext()
(di TODO: langkah 13 buat program tonton berikutnya):
programBuilder.setLastEngagementTimeUtcMillis(System.currentTimeMillis())
programBuilder.setWatchNextType(metadata.playbackPositionMillis?.let { position ->
if (position > 0 && metadata.playbackDurationMillis?.let { it > 0 } == true) {
Log.d(TAG, "Inferred watch next type: CONTINUE")
TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE
} else {
Log.d(TAG, "Inferred watch next type: UNKNOWN")
WatchNextProgram.WATCH_NEXT_TYPE_UNKNOWN
}
} ?: TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_NEXT)
// This must match the desired intent filter in the manifest for VIEW intent action
programBuilder.setIntentUri(Uri.parse(
"https://${context.getString(R.string.host_name)}/program/${metadata.id}"))
// Build the program with all the metadata
val updatedProgram = programBuilder.build()
Setelah memanggil metode build(
) di WatchNextProgram.Builder
, WatchNextProgam
akan dibuat. Anda dapat memublikasikannya ke baris Tonton Berikutnya dengan PreviewChannelHelper
.
Tambahkan kode berikut (di TODO: langkah 14.1 buat program tonton berikutnya):
val programId = PreviewChannelHelper(context)
.publishWatchNextProgram(updatedProgram)
Log.d(TAG, "Added program to watch next row: $updatedProgram")
programId
Atau jika program tersebut ada, cukup update (di TODO: langkah 14.2 update program tonton berikutnya)
PreviewChannelHelper(context)
.updateWatchNextProgram(updatedProgram, existingProgram.id)
Log.d(TAG, "Updated program in watch next row: $updatedProgram")
existingProgram.id
Menghapus program Tonton Berikutnya
Saat pengguna selesai memutar video, Anda harus membersihkan saluran Tonton Berikutnya. Ini hampir sama dengan menghapus PreviewProgram
.
Gunakan buildWatchNextProgramUri()
untuk membuat Uri
yang melakukan penghapusan. (Tidak ada API yang dapat digunakan dalam PreviewChannelHelper
untuk menghapus program Tonton Berikutnya.)
Ganti kode yang ada dalam metode removeFromWatchNext()
dari class TvLauncherUtils
dengan pernyataan di bawah (di TODO: langkah 15 hapus program):
val programUri = TvContractCompat.buildWatchNextProgramUri(it.id)
val deleteCount = context.contentResolver.delete(
programUri, null, null)
Menjalankan aplikasi
Pilih step_3 dalam konfigurasi dan jalankan aplikasi.
Tonton video dari koleksi Anda selama beberapa detik dan jeda pemutar (spasi jika Anda menggunakan emulator). Saat kembali ke layar utama, Anda akan melihat bahwa film telah ditambahkan ke saluran Tonton Berikutnya. Pilih film yang sama dari saluran Tonton Berikutnya dan pemutaran seharusnya dilanjutkan dari tempat Anda menjedanya. Setelah Anda menonton seluruh film, film tersebut akan dihapus dari saluran Tonton Berikutnya. Cobalah saluran Tonton Berikutnya menggunakan skenario pengguna yang berbeda.
Yang telah Anda pelajari
- Cara menambahkan program ke saluran Tonton Berikutnya untuk mendorong interaksi.
- Cara mengupdate program di saluran Tonton Berikutnya.
- Cara menghapus program dari saluran Tonton Berikutnya.
Apa selanjutnya?
Setelah menyelesaikan codelab, buat aplikasi Anda sendiri. Ganti feed media dan model data dengan milik Anda dan konversikan menjadi saluran dan program untuk Penyedia TV.
Untuk mempelajari lebih lanjut, buka dokumentasi.