本指南適用於開發人員,說明如何使用 Engage SDK 整合建議的影片內容,在 Google 途徑 (例如電視、行動裝置和平板電腦) 中提供建議體驗。
推薦內容會利用推薦叢集,在一個 UI 群組中顯示多個應用程式的電影和電視節目。每個開發合作夥伴最多可在每個建議叢集中播送 25 個實體,且每個要求最多可有 7 個建議叢集。
事前作業
開始之前,請先完成下列步驟。 1. 確認應用程式的目標 API 級別為 19 以上,才能進行這項整合。
將
com.google.android.engage程式庫新增至應用程式。整合時需要使用不同的 SDK:一個用於行動應用程式,另一個用於電視應用程式。
行動裝置適用
dependencies { implementation 'com.google.android.engage:engage-core:1.5.9 }電視
dependencies { implementation 'com.google.android.engage:engage-tv:1.0.5 }在
AndroidManifest.xml檔案中,將 Engage 服務環境設為正式版。行動裝置 APK
<meta-data android:name="com.google.android.engage.service.ENV" android:value="PRODUCTION"> </meta-data>For tv apk
<meta-data android:name="com.google.android.engage.service.ENV" android:value="PRODUCTION"> </meta-data>在前景服務上執行發布作業。
每天最多發布一次建議資料,觸發條件為
- 使用者當天首次登入。(或)
- 使用者開始與應用程式互動時。
整合
AppEngagePublishClient發布建議叢集。使用 publishRecommendationClusters 方法發布建議物件。
使用 isServiceAvailable()2 檢查服務是否可供整合。
val client = AppEngagePublishClient(context)
client.isServiceAvailable().addOnCompleteListener { task ->
if (task.isSuccessful) {
// Handle IPC call success
if(task.result) {
// Service is available on the device, proceed with content publish
// calls.
client.publishRecommendationClusters(recommendationRequest)
} else {
// Service is not available
}
} else {
// The IPC call itself fails, proceed with error handling logic here,
// such as retry.
}
}
推薦叢集和發布要求
叢集是實體的邏輯分組。下列程式碼範例說明如何根據偏好建立叢集,以及如何為所有叢集建立發布要求。
// cluster for popular movies
val recommendationCluster1 = RecommendationCluster
.Builder()
.addEntity(movie1)
.addEntity(movie2)
.addEntity(movie3)
.addEntity(movie4)
.addEntity(tvShow)
// This cluster is meant to be used as an individual provider row
.setRecommendationClusterType(TYPE_PROVIDER_ROW)
.setTitle("Popular Movies")
.build()
// cluster for live TV programs
val recommendationCluster2 = RecommendationCluster
.Builder()
.addEntity(liveTvProgramEntity1)
.addEntity(liveTvProgramEntity2)
.addEntity(liveTvProgramEntity3)
.addEntity(liveTvProgramEntity4)
.addEntity(liveTvProgramEntity5)
// This cluster is meant to be used as an individual provider row
.setRecommendationClusterType(TYPE_PROVIDER_ROW)
.setTitle("Popular Live TV Programs")
.build()
// creating a publishing request
val recommendationRequest = PublishRecommendationClustersRequest
.Builder()
.setSyncAcrossDevices(true)
.setAccountProfile(accountProfile)
.addRecommendationCluster(recommendationCluster1)
.addRecommendationCluster(recommendationCluster2)
.build()
建立帳戶設定檔
提供帳戶和個人資料資訊,即可在 Google TV 上享有個人化體驗。使用 AccountProfile 提供:
- 帳戶 ID:代表應用程式內使用者帳戶的專屬 ID。可以是實際帳戶 ID,也可以是適當的模糊處理版本。
- 設定檔 ID (選填):如果應用程式支援單一帳戶中的多個設定檔,請提供特定使用者設定檔的專屬 ID。
- 地區設定(選用):您可以選擇提供使用者偏好的語言。
如果您在
RecommendationRequest中傳送MediaActionFeedEntity,這個欄位就非常實用。
// If app only supports account
val accountProfile = AccountProfile.Builder()
.setAccountId("account_id")
.build();
// If app supports both account and profile
val accountProfile = AccountProfile.Builder()
.setAccountId("account_id")
.setProfileId("profile_id")
.build();
// set Locale
val accountProfile = AccountProfile.Builder()
.setAccountId("account_id")
.setProfileId("profile_id")
.setLocale("en-US")
.build();
服務收到要求後,系統會在單一交易中執行以下動作:
- 移除開發合作夥伴提供的現有
RecommendationsCluster資料。 - 剖析要求所提供的資料並儲存在更新後的
RecommendationsCluster。如果發生錯誤,整個要求都會遭到拒絕,現有狀態則維持不變。
跨裝置同步
SyncAcrossDevices 旗標可控制是否要將使用者的建議叢集資料分享給 Google TV,並在電視、手機、平板電腦等裝置上使用。建議必須設為 true 才能運作。
取得同意聲明
媒體應用程式必須提供明確的設定,讓使用者啟用或停用跨裝置同步功能。向使用者說明優點,並儲存使用者的偏好設定一次,然後在 publishRecommendations 要求中套用。如要充分運用跨裝置功能,請確認應用程式已取得使用者同意聲明,並啟用 SyncAcrossDevices 以 true。
刪除影片探索資料
如要在標準 60 天保留期限前,從 Google TV 伺服器手動刪除使用者資料,請使用 client.deleteClusters() 方法。服務收到要求後,會刪除帳戶設定檔或整個帳戶的所有現有影片探索資料。
DeleteReason 列舉會定義資料刪除原因。
下列程式碼會在登出時移除建議。
// If the user logs out from your media app, you must make the following call
// to remove recommendations data from the current google TV device,
// otherwise, the recommendations data persists on the current
// google TV device until 60 days later.
client.deleteClusters(
new DeleteClustersRequest.Builder()
.setAccountProfile(AccountProfile())
.setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
.build()
)
// If the user revokes the consent to share data with Google TV,
// you must make the following call to remove recommendations data from
// all current google TV devices. Otherwise, the recommendations data persists
// until 60 days later.
client.deleteClusters(
new DeleteClustersRequest.Builder()
.setAccountProfile(AccountProfile())
.setReason(DeleteReason.DELETE_REASON_LOSS_OF_CONSENT)
.build()
)
建立實體
SDK 定義了不同實體,用來代表各種項目類型。建議叢集支援下列實體:
MediaActionFeedEntityMovieEntityTvShowEntityLiveTvChannelEntityLiveTvProgramEntity
說明
為每個實體提供簡短說明,使用者將游標懸停在實體上時,系統就會顯示這些說明,提供額外詳細資料。
平台專屬的播放 URI
為每個支援的平台 (Android TV、Android 或 iOS) 建立播放 URI。 這樣一來,系統就能在相應平台上選取適當的 URI 來播放影片。
如果所有平台的播放 URI 都相同,請為每個平台重複此步驟。
// Required. Set this when you want recommended 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()
// Optional. Set this when you want recommended entities to show up on
// Google TV Android app
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 recommended 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)
// Provide appropriate rating for the system.
val contentRating = new RatingSystem
.Builder()
.setAgencyName("MPAA")
.setRating("PG-13")
.build()
代表圖片
海報圖片需要 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)
建議原因
您可以選擇提供建議原因,Google TV 可據此建構原因,向使用者建議特定電影或電視節目。
//Allows us to construct reason: "Because it is top 10 on your Channel"
val topOnPartner = RecommendationReasonTopOnPartner
.Builder()
.setNum(10) //any valid integer value
.build()
//Allows us to construct reason: "Because it is popular on your Channel"
val popularOnPartner = RecommendationReasonPopularOnPartner
.Builder()
.build()
//Allows us to construct reason: "New to your channel, or Just added"
val newOnPartner = RecommendationReasonNewOnPartner
.Builder()
.build()
//Allows us to construct reason: "Because you watched Star Wars"
val watchedSimilarTitles = RecommendationReasonWatchedSimilarTitles
.addSimilarWatchedTitleName("Movie or TV Show Title")
.addSimilarWatchedTitleName("Movie or TV Show Title")
.Builder()
.build()
//Allows us to construct reason: "Recommended for you by ChannelName"
val recommendedForUser = RecommendationReasonRecommendedForUser
.Builder()
.build()
val watchAgain = RecommendationReasonWatchAgain
.Builder()
.build()
val fromUserWatchList = RecommendationReasonFromUserWatchlist
.Builder()
.build()
val userLikedOnPartner = RecommendationReasonUserLikedOnPartner
.Builder()
.setTitleName("Movie or TV Show Title")
.build()
val generic = RecommendationReasonGeneric.Builder().build()
顯示時間範圍
如果實體只能在一段時間內使用,請設定自訂到期時間。如果沒有明確的到期時間,實體會在 60 天後自動過期並遭到清除。因此,只有在實體需要提早過期時,才設定到期時間。指定多個這類空檔。
val window1 = DisplayTimeWindow
.Builder()
.setStartTimeStampMillis(now()+ 1.days.toMillis())
.setEndTimeStampMillis(now()+ 30.days.toMillis())
val window2 = DisplayTimeWindow
.Builder()
.setEndTimeStampMillis(now()+ 30.days.toMillis())
val availabilityTimeWindows: List<DisplayTimeWindow> = listof(window1,window2)
DataFeedElementId
如果已將媒體目錄或媒體動作動態消息與 Google TV 整合,則不必為電影或電視節目建立個別實體,而是可以建立 MediaActionFeed 實體,其中包含必要欄位 DataFeedElementId。這個 ID 不得重複,且必須與媒體動作動態饋給中的 ID 相符,因為系統會根據這個 ID 識別已擷取的動態饋給內容,並執行媒體內容查詢。
val id = "dataFeedEleemntId"
MovieEntity
以下範例說明如何建立包含所有必要欄位的 MovieEntity:
val movieEntity = MovieEntity.Builder()
.setName("Movie name")
.setDescription("A sentence describing movie.")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Suppose the duration is 2 hours, it is 72000000 in milliseconds
.setDurationMills(72000000)
.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);
//Suppose release date is 11-02-2025
val releaseDate = 1739233800000L
val movieEntity = MovieEntity.Builder()
...
.addGenres(genres)
.setReleaseDateEpochMillis(releaseDate)
.addContentRatings(contentRatings)
.setRecommendationReason(topOnPartner or watchedSimilarTitles)
.addAllAvailabilityTimeWindows(availabilityTimeWindows)
.build()
TvShowEntity
以下範例說明如何建立包含所有必要欄位的 TvShowEntity:
val tvShowEntity = TvShowEntity.Builder()
.setName("Show title")
.setDescription("A sentence describing TV Show.")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
.build();
你也可以選擇提供其他資料,例如類型、內容分級、推薦原因、特價、季數或供應時間範圍,Google TV 可能會使用這些資料來改善顯示方式或篩選結果。
val genres = Arrays.asList("Action", "Science fiction");
val rating1 = RatingSystem.Builder()
.setAgencyName("MPAA")
.setRating("pg-13")
.build();
val price = Price.Builder()
.setCurrentPrice("$14.99")
.setStrikethroughPrice("$16.99")
.build();
val contentRatings = Arrays.asList(rating1);
val seasonCount = 5;
val tvShowEntity = TvShowEntity.Builder()
...
.addGenres(genres)
.addContentRatings(contentRatings)
.setRecommendationReason(topOnPartner or watchedSimilarTitles)
.addAllAvailabilityTimeWindows(availabilityTimeWindows)
.setSeasonCount(seasonCount)
.setPrice(price)
.build()
MediaActionFeedEntity
以下範例說明如何建立 MediaActionFeedEntity,並填入所有必要欄位:
val mediaActionFeedEntity = MediaActionFeedEntity.Builder()
.setDataFeedElementId(id)
.build()
視需要提供其他資料,例如說明、建議原因和顯示時間範圍,Google TV 可能會使用這些資料來改善顯示效果或進行篩選。
val mediaActionFeedEntity = MediaActionFeedEntity.Builder()
.setName("Movie name or TV Show name")
.setDescription("A sentence describing an entity")
.setRecommendationReason(topOnPartner or watchedSimilarTitles)
.addPosterImages(images)
.build()
LiveTvChannelEntity
代表直播電視頻道。以下範例說明如何建立包含所有必要欄位的 LiveTvChannelEntity:
val liveTvChannelEntity = LiveTvChannelEntity.Builder()
.setName("Channel Name")
// ID of the live TV channel
.setEntityId("https://www.example.com/channel/12345")
.setDescription("A sentence describing this live TV channel.")
// channel playback uri must contain at least PlatformType.TYPE_ANDROID_TV
.addPlatformSpecificPlaybackUri(channelPlaybackUris)
.addLogoImage(logoImage)
.build()
視需要提供額外資料,例如內容分級或推薦原因。
val rating1 = RatingSystem.Builder()
.setAgencyName("MPAA")
.setRating("pg-13")
.build()
val contentRatings = Arrays.asList(rating1)
val liveTvChannelEntity = LiveTvChannelEntity.Builder()
...
.addContentRatings(contentRatings)
.setRecommendationReason(topOnPartner)
.build()
LiveTvProgramEntity
這代表直播電視頻道正在或預計播出的直播電視節目卡片。以下範例說明如何建立 LiveTvProgramEntity,並填入所有必要欄位:
val liveTvProgramEntity = LiveTvProgramEntity.Builder()
// First set the channel information
.setChannelName("Channel Name")
.setChanelId("https://www.example.com/channel/12345")
// channel playback uri must contain at least PlatformType.TYPE_ANDROID_TV
.addPlatformSpecificPlaybackUri(channelPlaybackUris)
.setChannelLogoImage(channelLogoImage)
// Then set the program or card specific information.
.setName("Program Name")
.setEntityId("https://www.example.com/schedule/123")
.setDescription("Program Desccription")
.addAvailabilityTimeWindow(
DisplayTimeWindow.Builder()
.setStartTimestampMillis(1756713600000L)// 2025-09-01T07:30:00+0000
.setEndTimestampMillis(1756715400000L))// 2025-09-01T08:00:00+0000
.addPosterImage(programImage)
.build()
您可以視需要提供額外資料,例如內容分級、類型或推薦原因。
val rating1 = RatingSystem.Builder()
.setAgencyName("MPAA")
.setRating("pg-13")
.build()
val contentRatings = Arrays.asList(rating1)
val genres = Arrays.asList("Action", "Science fiction")
val liveTvProgramEntity = LiveTvProgramEntity.Builder()
...
.addContentRatings(contentRatings)
.addGenres(genres)
.setRecommendationReason(topOnPartner)
.build()
開發人員只要按照這些步驟操作,就能將影片內容建議順利整合至 Google TV,進而提升使用者探索和參與度,並在所有裝置上為使用者提供一致的個人化觀看體驗。