Engage SDK for TV 整合指南

「繼續觀看」會利用接續叢集,在單一 UI 群組中,顯示多個應用程式中的未播完影片,以及同一電視季的下一集。您可以在這個接續叢集中宣傳這些實體。請按照本指南操作,瞭解如何透過 Engage SDK 的「繼續觀看」體驗,提升使用者參與度。

步驟 1:前置作業

在開始之前,請先完成下列步驟:

請確認應用程式指定的 API 級別為 19 以上,才能進行此整合

  1. com.google.android.engage 程式庫新增至應用程式:

    整合時需要使用不同的 SDK:一個用於行動應用程式,另一個用於電視應用程式。

    行動裝置

    
      dependencies {
        implementation 'com.google.android.engage:engage-core:1.5.5
      }
    

    電視

    
      dependencies {
        implementation 'com.google.android.engage:engage-tv:1.0.2
      }
    
  2. AndroidManifest.xml 檔案中將 Engage 服務環境設為正式版。

    行動裝置

    
    <meta-data
          android:name="com.google.android.engage.service.ENV"
          android:value="PRODUCTION">
    </meta-data>
    

    電視

    
    <meta-data
        android:name="com.google.android.engage.service.ENV"
        android:value="PRODUCTION">
    </meta-data>
    
  3. 為 TV APK 新增 WRITE_EPG_DATA 權限

      <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
    
  4. 使用 androidx.work 等背景服務進行排程,確保內容發布可靠。

  5. 如要提供順暢的觀看體驗,請在發生下列事件時發布繼續觀看資料:

    1. 首次登入:使用者首次登入時,發布其資料可確保他們的觀看記錄立即可用。
    2. 建立或切換設定檔 (多重設定檔應用程式):如果應用程式支援多重設定檔,請在使用者建立或切換設定檔時發布資料。這樣一來,每位使用者都能享有個人化體驗。
    3. 影片播放中斷:為了協助使用者繼續觀看,請在使用者暫停或停止影片,或是在播放期間退出應用程式時發布資料。
    4. 繼續觀看列更新 (如有支援):當使用者從「繼續觀看」列移除項目時,請發布更新的資料,反映該變更。這樣一來,應用程式就能確保通知匣持續提供相關且個人化的內容。
    5. 影片完成度:
      1. 針對電影,請從「繼續觀看」區域移除已看完的電影。如果電影屬於系列作品,請加入下一集電影,讓使用者持續觀看。
      2. 針對單集節目,請移除已播完的集數,並新增該系列的下一集 (如有),鼓勵觀眾繼續觀看。

整合

AccountProfile

如要在 Google TV 上提供個人化的「繼續觀看」體驗,請提供帳戶和個人資料資訊。使用 AccountProfile 提供下列資訊:

  1. 帳戶 ID:代表應用程式中使用者帳戶的專屬 ID。這可以是實際帳戶 ID 或經過適當模糊處理的版本。

  2. 設定檔 ID (選用):如果應用程式支援單一帳戶中的多個設定檔,請為特定使用者設定檔提供專屬 ID (同樣可以是真實或模糊處理的 ID)。

// If your app only supports account
val accountProfile = AccountProfile.Builder()
      .setAccountId("your_users_account_id")
      .build()
// If your app supports both account and profile
val accountProfile = AccountProfile.Builder()
      .setAccountId("your_users_account_id")
      .setProfileId("your_users_profile_id")
.build()

建立實體

SDK 定義了不同實體,用來代表各種項目類型。接續叢集支援下列實體:

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

指定這些實體的平台專屬 URI 和海報圖片。

此外,如果尚未為各個平台 (例如 Android TV、Android 或 iOS) 建立播放 URI,也請一併建立。因此,當使用者在各個平台上繼續觀看時,應用程式會使用指定的播放 URI 播放影片內容。

// 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)

海報圖片需要 URI 和像素尺寸 (高度和寬度)。提供多張海報圖片,以便鎖定不同板型規格,但請務必確保所有圖片都維持 16:9 的顯示比例,且高度至少為 200 像素,以便正確顯示「繼續觀看」實體,尤其是在 Google 的娛樂空間中。高度小於 200 像素的圖片可能不會顯示。


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

以下範例說明如何建立包含所有必要欄位的 MovieEntity

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)
   .build()

提供類型和內容評等等詳細資料,可讓 Google TV 以更生動的方式展示你的內容,並與合適的觀眾連結。

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

除非您指定較短的到期時間,否則實體會自動保留 60 天。只有在需要在預設期限前移除實體時,才設定自訂到期日。

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

本範例說明如何建立包含所有必要欄位的 TvEpisodeEntity

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")
    .build();

集數字串 (例如 "2") 和季別字串 (例如 "1") 會展開為適當的格式,然後顯示在繼續觀看資訊卡上。請注意,這些字串必須是數字,請勿輸入「e2」、「episode 2」、「s1」或「season 1」。

如果特定電視節目只有一季,請將季別編號設為 1。

為提高觀眾在 Google TV 上找到你內容的機會,建議你提供額外資料,例如類型、內容分級和播映時間,因為這些詳細資料有助於改善顯示和篩選選項。

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

以下範例說明如何建立包含所有必要欄位的 VideoClipEntity

VideoClipEntity 代表使用者自製的短片,例如 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)
    .build();

您可以選擇設定創作者、創作者圖片、建立時間 (以毫秒為單位) 或播映時間視窗。

LiveStreamingVideoEntity

以下範例說明如何建立含有所有必要欄位的 LiveStreamingVideoEntity

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)
    .build();

您可以選擇為直播實體設定開始時間、廣播者、廣播者圖示或播映時間視窗。

如要進一步瞭解屬性和規定,請參閱 API 參考資料

提供接續叢集資料

AppEngagePublishClient 負責發布接續叢集。您可以使用 publishContinuationCluster() 方法發布 ContinuationCluster 物件。

首先,您應使用 isServiceAvailable() 檢查服務是否可供整合。

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

服務收到要求後,系統會在單一交易中執行以下動作:

  • 移除開發合作夥伴提供的現有 ContinuationCluster 資料。
  • 剖析要求所提供的資料並儲存在更新後的 ContinuationCluster 中。

如果發生錯誤,整個要求都會遭到拒絕,現有狀態則維持不變。

發布 API 屬於更新/插入 API,會取代現有內容。如果您需要更新 ContinuationCluster 中的特定實體,就必須再次發布所有實體。

只有成人帳戶可以提供 ContinuationCluster 資料。只有在 AccountProfile 屬於成人時才發布。

跨裝置同步

SyncAcrossDevices 標記

這個標記可控制使用者的 ContinuationCluster 資料是否會在其裝置 (電視、手機、平板電腦等) 之間同步。預設為 false,表示系統預設會停用跨裝置同步處理功能。

值:

  • true:系統會在使用者的所有裝置上分享 ContinuationCluster 資料,提供順暢的觀看體驗。我們強烈建議您使用這個選項,以獲得最佳跨裝置體驗。
  • false:ContinuationCluster 資料僅限於目前裝置。

媒體應用程式必須提供明確的設定,讓使用者啟用/停用跨裝置同步功能。向使用者說明優點,並儲存使用者的偏好設定,然後在 publishContinuationCluster 中加以套用。

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

如要充分發揮跨裝置功能的效益,請確保應用程式已取得使用者的同意,並將 SyncAcrossDevices 設為 true。這樣一來,內容就能在不同裝置間順暢同步,進而提供更優質的使用者體驗,並提高參與度。舉例來說,有位合作夥伴在導入這項功能後,由於內容在多部裝置上顯示,因此「繼續觀看」點擊次數增加了 40%。

刪除影片探索資料

如要在標準 60 天保留期限前,手動從 Google TV 伺服器刪除使用者資料,請使用 client.deleteClusters() 方法。收到要求後,服務會刪除帳戶個人資料或整個帳戶的所有現有影片探索資料。

DeleteReason 列舉會定義資料刪除的原因。以下程式碼會在登出時移除繼續觀看資料。


// 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(
  new DeleteClustersRequest.Builder()
        .setAccountProfile(AccountProfile())
        .setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
        .setSyncAcrossDevices(true)
        .build()
)

測試

使用驗證應用程式,確保 Engage SDK 整合功能正常運作。這個 Android 應用程式提供工具,協助您驗證資料,並確認廣播意圖是否正確處理。

叫用發布 API 後,請檢查驗證應用程式,確認資料是否正確發布。接續叢集應會顯示為應用程式介面中的獨立資料列。

  • 確認「Engage Service Flag」在應用程式的 Android 資訊清單檔案中設為正式版。
  • 安裝並開啟 Engage Verify 應用程式
  • 如果 isServiceAvailablefalse,請按一下「切換」按鈕啟用。
  • 輸入應用程式的套件名稱,即可在開始發布時自動查看已發布的資料。
  • 在應用程式中測試下列動作:
    • 登入。
    • 切換設定檔(如適用)。
    • 開始播放影片,然後暫停或返回首頁。
    • 在影片播放期間關閉應用程式。
    • 從「繼續觀看」列中移除項目 (如有支援)。
  • 完成每個動作後,請確認應用程式已叫用 publishContinuationClusters API,且資料已正確顯示在驗證應用程式中。
  • 如果實作實體正確,驗證應用程式會顯示綠色「All Good」勾號。

    驗證應用程式成功螢幕截圖
    圖 1. 驗證應用程式成功
  • 驗證應用程式會標示任何有問題的實體。

    驗證應用程式錯誤螢幕截圖
    圖 2. 驗證應用程式錯誤
  • 如要排解發生錯誤的實體,請使用電視遙控器選取並點選驗證應用程式中的實體。系統會顯示特定問題並以紅色醒目顯示,供你查看 (請參閱下方範例)。

    驗證應用程式錯誤詳細資料
    圖 3. 驗證應用程式錯誤詳細資料