Mengintegrasikan Lanjutkan Menonton di Android TV

Panduan ini membahas cara mengintegrasikan Lanjutkan Menonton ke dalam aplikasi Android TV Anda menggunakan Engage SDK.

Persiapan

Selesaikan petunjuk Persiapan di Panduan memulai.

Integrasi

Membuat entity

SDK telah menentukan entity yang berbeda untuk mewakili setiap jenis item. Cluster lanjutan mendukung entity berikut:

  1. MovieEntity
  2. TvEpisodeEntity
  3. LiveStreamingVideoEntity
  4. VideoClipEntity

Tentukan URI khusus platform dan gambar poster untuk entity ini.

Selain itu, buat URI pemutaran untuk setiap platform—seperti Android TV, Android, atau iOS—jika Anda belum melakukannya. Jadi, saat pengguna melanjutkan menonton di setiap platform, aplikasi akan menggunakan URI pemutaran yang ditargetkan untuk memutar konten video.

// Required. Set this when you want continue watching entities to show up on
// Google TV
val playbackUriTv = PlatformSpecificUri.Builder()
    .setPlatformType(PlatformType.TYPE_ANDROID_TV)
    .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_tv"))
    .build()

// Required. Set this when you want continue watching entities to show up on
// Google TV Android app, Entertainment Space, Playstore Widget
val playbackUriAndroid = PlatformSpecificUri.Builder()
    .setPlatformType(PlatformType.TYPE_ANDROID_MOBILE)
    .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_android"))
    .build()

// Optional. Set this when you want continue watching entities to show up on
// Google TV iOS app
val playbackUriIos = PlatformSpecificUri.Builder()
    .setPlatformType(PlatformType.TYPE_IOS)
    .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_ios"))
    .build()

val platformSpecificPlaybackUris =
    Arrays.asList(playbackUriTv, playbackUriAndroid, playbackUriIos)

Gambar poster memerlukan URI dan dimensi piksel (tinggi dan lebar). Targetkan faktor bentuk yang berbeda dengan menyediakan beberapa gambar poster, tetapi pastikan semua gambar mempertahankan rasio aspek 16:9 dan tinggi minimum 200 piksel untuk tampilan entity "Lanjutkan Menonton" yang benar, terutama dalam Google's Entertainment Space. Gambar dengan tinggi kurang dari 200 piksel mungkin tidak ditampilkan.

val images = Arrays.asList(
    Image.Builder()
        .setImageUri(Uri.parse("http://www.example.com/entity_image1.png"))
        .setImageHeightInPixel(300)
        .setImageWidthInPixel(169)
        .build(),
    Image.Builder()
        .setImageUri(Uri.parse("http://www.example.com/entity_image2.png"))
        .setImageHeightInPixel(640)
        .setImageWidthInPixel(360)
        .build()
    // Consider adding other images for different form factors
)

MovieEntity

Contoh ini menunjukkan cara membuat MovieEntity dengan semua kolom yang diperlukan:

val movieEntity = MovieEntity.Builder()
   .setWatchNextType(WatchNextType.TYPE_CONTINUE)
   .setName("Movie name")
   .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
   .addPosterImages(images)
   // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
   .setLastEngagementTimeMillis(1701388800000)
   // Suppose the duration is 2 hours, it is 72000000 in milliseconds
   .setDurationMills(72000000)
   // Suppose last playback offset is 1 hour, 36000000 in milliseconds
   .setLastPlayBackPositionTimeMillis(36000000)
   .setCallToActionText("Resume")
   .addTag("Action")
   .build()

Dengan memberikan detail seperti genre dan rating konten, Google TV dapat menampilkan konten Anda dengan cara yang lebih dinamis dan menghubungkannya dengan penonton yang tepat.

val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val movieEntity = MovieEntity.Builder()
    ...
    .addGenres(genres)
    .addContentRatings(contentRatings)
    .build()

Entity akan otomatis tetap tersedia selama 60 hari, kecuali jika Anda menentukan waktu habis masa berlaku yang lebih singkat. Hanya tetapkan masa berlaku kustom jika Anda ingin entity dihapus sebelum periode default ini.

// Set the expiration time to be now plus 30 days in milliseconds
val expirationTime = DisplayTimeWindow.Builder()
    .setEndTimestampMillis(now().toMillis()+2592000000).build()
val movieEntity = MovieEntity.Builder()
    ...
    .addAvailabilityTimeWindow(expirationTime)
    .build()

TvEpisodeEntity

Contoh ini menunjukkan cara membuat TvEpisodeEntity dengan semua kolom yang diperlukan:

val tvEpisodeEntity = TvEpisodeEntity.Builder()
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Episode name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(72000000) // 2 hours in milliseconds
    // 45 minutes and 15 seconds in milliseconds is 2715000
    .setLastPlayBackPositionTimeMillis(2715000)
    .setEpisodeNumber("2")
    .setSeasonNumber("1")
    .setShowTitle("Title of the show")
    .setCallToActionText("Resume")
    .addTag("Comedy")
    .build()

String nomor episode (seperti "2") dan string nomor musim (seperti "1") akan diperluas ke bentuk yang tepat sebelum ditampilkan di kartu lanjutkan menonton. Perhatikan bahwa string tersebut harus berupa string numerik, jangan masukkan "e2", atau "episode 2", atau "s1" atau "season 1".

Jika acara TV tertentu hanya memiliki satu musim, tetapkan nomor musim sebagai 1.

Untuk memaksimalkan peluang penonton menemukan konten Anda di Google TV, pertimbangkan untuk memberikan data tambahan seperti genre, rating konten, dan periode waktu ketersediaan, karena detail ini dapat meningkatkan tampilan dan opsi pemfilteran.

val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val tvEpisodeEntity = TvEpisodeEntity.Builder()
    ...
    .addGenres(genres)
    .addContentRatings(contentRatings)
    .setSeasonTitle("Season Title")
    .setShowTitle("Show Title")
    .build()

VideoClipEntity

Berikut adalah contoh pembuatan VideoClipEntity dengan semua kolom yang diperlukan.

VideoClipEntity mewakili klip yang dibuat pengguna seperti video YouTube.

val videoClipEntity = VideoClipEntity.Builder()
    .setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform"))
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Video clip name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(600000) //10 minutes in milliseconds
    .setLastPlayBackPositionTimeMillis(300000) //5 minutes in milliseconds
    .addContentRating(contentRating)
    .setCallToActionText("Resume")
    .addTag("Vlog")
    .build()

Anda dapat secara opsional menetapkan kreator, gambar kreator, waktu pembuatan dalam milidetik, atau periode waktu ketersediaan .

LiveStreamingVideoEntity

Berikut adalah contoh pembuatan LiveStreamingVideoEntity dengan semua kolom yang diperlukan.

val liveStreamingVideoEntity = LiveStreamingVideoEntity.Builder()
    .setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform"))
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Live streaming name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(72000000) //2 hours in milliseconds
    .setLastPlayBackPositionTimeMillis(36000000) //1 hour in milliseconds
    .addContentRating(contentRating)
    .setCallToActionText("Resume")
    .addTag("Live")
    .build()

Secara opsional, Anda dapat menetapkan waktu mulai, penyiar, ikon penyiar, atau periode waktu ketersediaan untuk entity live streaming.

Untuk mengetahui informasi mendetail tentang atribut dan persyaratan, lihat referensi API.

Memberikan data cluster Lanjutan

AppEngagePublishClient bertanggung jawab untuk memublikasikan cluster Lanjutan. Anda menggunakan metode publishContinuationCluste untuk memublikasikan objek ContinuationCluster.

Pastikan untuk menginisialisasi klien dan memeriksa ketersediaan layanan seperti yang dijelaskan dalam Panduan memulai.

client.publishContinuationCluster(
    PublishContinuationClusterRequest
        .Builder()
        .setContinuationCluster(
            ContinuationCluster.Builder()
                .setAccountProfile(accountProfile)
                .addEntity(movieEntity1)
                .addEntity(movieEntity2)
                .addEntity(tvEpisodeEntity1)
                .addEntity(tvEpisodeEntity2)
                .setSyncAcrossDevices(true)
                .build()
        )
        .build()
)

Saat layanan menerima permintaan tersebut, tindakan berikut akan terjadi dalam satu transaksi:

  • Data ContinuationCluster yang ada dari partner developer akan dihapus.
  • Data dari permintaan akan diuraikan dan disimpan di ContinuationCluster yang diperbarui.

Jika terjadi error, seluruh permintaan akan ditolak dan status yang ada dipertahankan.

API publikasi adalah API penyisipan yang menggantikan konten yang sudah ada. Jika Anda perlu memperbarui entity tertentu di cluster lanjutan, Anda harus memublikasikan semua entity lagi.

Data cluster lanjutan hanya boleh diberikan untuk akun dewasa. Publikasikan hanya jika profil akun milik orang dewasa.

Sinkronisasi lintas perangkat

setSyncAcrossDevices mengontrol apakah data ContinuationCluster pengguna disinkronkan di seluruh perangkat seperti TV, ponsel, tablet, dll. Sinkronisasi lintas perangkat dinonaktifkan secara default.

Nilai:

  • true: Data cluster lanjutan dibagikan di seluruh perangkat pengguna untuk pengalaman menonton yang lancar. Sebaiknya gunakan opsi ini untuk pengalaman lintas perangkat terbaik.
  • false: Data cluster lanjutan dibatasi untuk perangkat saat ini.

Selain itu, Anda harus memberikan AccountProfile dengan ID akun agar konten dapat disinkronkan antarperangkat. Lihat Membuat profil akun.

Aplikasi media harus menyediakan setelan yang jelas untuk mengaktifkan atau menonaktifkan sinkronisasi lintas perangkat. Jelaskan manfaatnya kepada pengguna dan simpan preferensi pengguna sekali saja, lalu terapkan di publishContinuationCluster sebagaimana mestinya.

// Example to allow cross device syncing.
client.publishContinuationCluster(
    PublishContinuationClusterRequest
        .Builder()
        .setContinuationCluster(
            ContinuationCluster.Builder()
                .setAccountProfile(accountProfile)
                .setSyncAcrossDevices(true)
                .build()
        )
        .build()
)

Untuk memaksimalkan fitur lintas perangkat kami, pastikan aplikasi mendapatkan izin pengguna dan aktifkan SyncAcrossDevices ke true. Hal ini memungkinkan konten disinkronkan dengan lancar di seluruh perangkat, sehingga menghasilkan pengalaman pengguna yang lebih baik dan meningkatkan engagement. Misalnya, partner yang menerapkan hal ini mengalami peningkatan 40% dalam klik "lanjutkan menonton" karena konten mereka ditampilkan di beberapa perangkat.

Menghapus data Engage

Untuk menghapus data pengguna secara manual dari server Google TV sebelum periode retensi standar 60 hari, gunakan metode deleteClusters. Setelah menerima permintaan, layanan akan menghapus semua data Engage yang ada untuk profil akun, atau untuk seluruh akun.

Enum DeleteReason menentukan alasan penghapusan data. Kode berikut menghapus data lanjutkan menonton saat logout.


// If the user logs out from your media app, you must make the following call
// to remove continue watching data from the current google TV device,
// otherwise, the continue watching data will persist on the current
// google TV device until 60 days later.
client.deleteClusters(
    DeleteClustersRequest.Builder()
        .setAccountProfile(AccountProfile())
        .setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
        .setSyncAcrossDevices(true)
        .build()
)

Pengujian

Gunakan aplikasi verifikasi untuk memverifikasi bahwa integrasi Engage SDK berfungsi dengan benar.

Setelah memanggil API publikasi, konfirmasi bahwa data Anda dipublikasikan dengan benar dengan memeriksa aplikasi verifikasi. Cluster lanjutan Anda akan ditampilkan sebagai baris terpisah dalam antarmuka aplikasi.

  • Uji tindakan berikut di aplikasi Anda:
    • Login.
    • Beralih antar profil(jika berlaku).
    • Mulai, lalu jeda video, atau kembali ke halaman beranda.
    • Tutup aplikasi selama pemutaran video.
    • Hapus item dari baris "Lanjutkan Menonton" (jika didukung).
  • Setelah setiap tindakan, konfirmasi bahwa aplikasi Anda memanggil publishContinuationClusters API dan data ditampilkan dengan benar di aplikasi verifikasi.
  • Aplikasi verifikasi menampilkan tanda centang hijau "Semua Oke" untuk entity yang diimplementasikan dengan benar.

    Screenshot Berhasil Aplikasi Verifikasi
    Gambar 1. Keberhasilan Aplikasi Verifikasi
  • Aplikasi verifikasi akan menandai entity yang bermasalah.

    Screenshot Error Aplikasi Verifikasi
    Gambar 2. Error Aplikasi Verifikasi
  • Untuk memecahkan masalah entity yang mengalami error, gunakan remote TV untuk memilih dan mengklik entity di aplikasi verifikasi. Masalah tertentu akan ditampilkan dan ditandai dengan warna merah untuk ditinjau (lihat contoh di bawah).

    Detail error Aplikasi Verifikasi
    Gambar 3. Detail Error Aplikasi Verifikasi