用於影片推薦的 Engage SDK

本指南適用於開發人員,說明如何使用 Engage SDK 整合建議的影片內容,在 Google 途徑 (例如電視、行動裝置和平板電腦) 中提供建議體驗。

推薦內容會利用推薦叢集,在一個 UI 群組中顯示多個應用程式的電影和電視節目。每個開發合作夥伴最多可在每個建議叢集中播送 25 個實體,且每個要求最多可有 7 個建議叢集。

事前作業

開始之前,請先完成下列步驟。 1. 確認應用程式的目標 API 級別為 19 以上,才能進行這項整合。

  1. 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
      }
    
  2. 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>
    
  3. 在前景服務上執行發布作業。

  4. 每天最多發布一次建議資料,觸發條件為

    1. 使用者當天首次登入。()
    2. 使用者開始與應用程式互動時。

整合

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 提供:

  1. 帳戶 ID:代表應用程式內使用者帳戶的專屬 ID。可以是實際帳戶 ID,也可以是適當的模糊處理版本。
  2. 設定檔 ID (選填):如果應用程式支援單一帳戶中的多個設定檔,請提供特定使用者設定檔的專屬 ID。
  3. 地區設定(選用):您可以選擇提供使用者偏好的語言。 如果您在 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 要求中套用。如要充分運用跨裝置功能,請確認應用程式已取得使用者同意聲明,並啟用 SyncAcrossDevicestrue

刪除影片探索資料

如要在標準 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 定義了不同實體,用來代表各種項目類型。建議叢集支援下列實體:

  1. MediaActionFeedEntity
  2. MovieEntity
  3. TvShowEntity
  4. LiveTvChannelEntity
  5. LiveTvProgramEntity

說明

為每個實體提供簡短說明,使用者將游標懸停在實體上時,系統就會顯示這些說明,提供額外詳細資料。

平台專屬的播放 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,進而提升使用者探索和參與度,並在所有裝置上為使用者提供一致的個人化觀看體驗。