Integrasi Konten dengan Saluran Layar Utama Android TV (Kotlin)

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.

aa0471dc91b5f815.png

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.

Download 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.

c0e57864138c1248.png

Memahami project awal

bd4f805254260df7.png

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.

  1. Hubungkan Android TV atau mulai emulator.
  1. Pilih konfigurasi step_1, pilih perangkat Android Anda dan tekan tombol run di panel menu. ba443677e48e0f00.png
  2. Anda akan melihat outline aplikasi TV sederhana dengan tiga koleksi video.

364574330c4e90a5.png

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:

  1. Mengambil feed media, yang mencakup gambar latar, koleksi media, dan metadata video. Informasi ini ditentukan di assets/media-feed.json
  2. Mengupdate instance TvMediaDatabase, yang menyimpan gambar latar, koleksi media, dan data video, ke masing-masing objeknya
  3. 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.

  1. 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 sebagai internalProviderId saluran. Untuk mengidentifikasi saluran yang ada, bandingkan internalProviderId dengan id koleksi. Salin dan tempel kode berikut ke dalam upsertChannel()di komentar kode // TODO: Step 1 create or find an existing channel.
val channelBuilder = if (existingChannel == null) {
   PreviewChannel.Builder()
} else {
   PreviewChannel.Builder(existingChannel)
}
  1. Tetapkan atribut di Builder saluran (misalnya, nama dan logo/ikon saluran). Nama tampilan muncul di layar utama tepat di bawah ikon saluran. Android TV menggunakan appLinkIntentUri 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()
  1. Panggil fungsi di class PreviewChannelHelper untuk menyisipkan saluran ke penyedia TV atau mengupdate-nya. Panggilan ke publishChannel() 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
  1. 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.

f14e903b0505a281.png

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.

faac02714aa36ab6.png

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.

e096c4d12a3d0a01.png

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:

  1. Kode contoh menautkan metadata dengan program pratinjau melalui contentId.
  2. Program Pratinjau disisipkan ke dalam saluran dengan memanggil setChannelId() di PreviewProgram.Builder().
  3. 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.

200e69351ce6a530.png

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.

44b6a6f24e4420e3.png

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.

6e43dc24a1ef0273.png

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.