「繼續觀看」會利用接續叢集,在單一 UI 群組中,顯示多個應用程式中的未播完影片,以及同一電視季的下一集。您可以在這個接續叢集中宣傳這些實體。請按照本指南操作,瞭解如何透過 Engage SDK 的「繼續觀看」體驗,提升使用者參與度。
步驟 1:前置作業
在開始之前,請先完成下列步驟:
請確認應用程式指定的 API 級別為 19 以上,才能進行此整合
將
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 }
在
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>
為 TV APK 新增
WRITE_EPG_DATA
權限<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
使用
androidx.work
等背景服務進行排程,確保內容發布可靠。如要提供順暢的觀看體驗,請在發生下列事件時發布繼續觀看資料:
- 首次登入:使用者首次登入時,發布其資料可確保他們的觀看記錄立即可用。
- 建立或切換設定檔 (多重設定檔應用程式):如果應用程式支援多重設定檔,請在使用者建立或切換設定檔時發布資料。這樣一來,每位使用者都能享有個人化體驗。
- 影片播放中斷:為了協助使用者繼續觀看,請在使用者暫停或停止影片,或是在播放期間退出應用程式時發布資料。
- 繼續觀看列更新 (如有支援):當使用者從「繼續觀看」列移除項目時,請發布更新的資料,反映該變更。這樣一來,應用程式就能確保通知匣持續提供相關且個人化的內容。
- 影片完成度:
- 針對電影,請從「繼續觀看」區域移除已看完的電影。如果電影屬於系列作品,請加入下一集電影,讓使用者持續觀看。
- 針對單集節目,請移除已播完的集數,並新增該系列的下一集 (如有),鼓勵觀眾繼續觀看。
整合
AccountProfile
如要在 Google TV 上提供個人化的「繼續觀看」體驗,請提供帳戶和個人資料資訊。使用 AccountProfile 提供下列資訊:
帳戶 ID:代表應用程式中使用者帳戶的專屬 ID。這可以是實際帳戶 ID 或經過適當模糊處理的版本。
設定檔 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 定義了不同實體,用來代表各種項目類型。接續叢集支援下列實體:
指定這些實體的平台專屬 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 應用程式
- 如果
isServiceAvailable
為false
,請按一下「切換」按鈕啟用。 - 輸入應用程式的套件名稱,即可在開始發布時自動查看已發布的資料。
- 在應用程式中測試下列動作:
- 登入。
- 切換設定檔(如適用)。
- 開始播放影片,然後暫停或返回首頁。
- 在影片播放期間關閉應用程式。
- 從「繼續觀看」列中移除項目 (如有支援)。
- 完成每個動作後,請確認應用程式已叫用 publishContinuationClusters API,且資料已正確顯示在驗證應用程式中。
如果實作實體正確,驗證應用程式會顯示綠色「All Good」勾號。
圖 1. 驗證應用程式成功 驗證應用程式會標示任何有問題的實體。
圖 2. 驗證應用程式錯誤 如要排解發生錯誤的實體,請使用電視遙控器選取並點選驗證應用程式中的實體。系統會顯示特定問題並以紅色醒目顯示,供你查看 (請參閱下方範例)。
圖 3. 驗證應用程式錯誤詳細資料