本指南適用對象為開發人員,提供使用 Engage SDK 與 Google TV 共用應用程式訂閱和授權資料的操作說明。使用者可以直接在 Google TV 的電視、行動裝置和平板電腦體驗中,找到自己有權觀看的內容,並讓 Google TV 為他們提供高度相關的內容推薦。
必要條件
您必須先導入媒體操作動態饋給,才能使用裝置授權 API。如果尚未完成,請完成媒體動作動態饋給的導入程序。
事前作業
開始前,請完成下列步驟:驗證應用程式目標 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 服務環境設為正式版。行動版 APK
<meta-data android:name="com.google.android.engage.service.ENV" android:value="PRODUCTION"> </meta-data>
電視版 APK
<meta-data android:name="com.google.android.engage.service.ENV" android:value="PRODUCTION"> </meta-data>
將 APK 傳送至 Google 前,請在 AndroidManifest.xml 檔案中將 Engage 服務環境設為正式版。為確保最佳效能和日後的相容性,請只在應用程式處於前景且使用者正在積極與其互動時 (例如應用程式啟動、登入後或在使用中) 發布資料。我們不建議從背景程序發布。
針對下列事件發布訂閱資訊:
- 使用者登入您的應用程式。
- 使用者在設定檔之間切換 (如果系統支援設定檔)。
- 使用者購買新的訂閱項目。
- 使用者升級現有訂閱項目。
- 使用者訂閱項目到期。
整合
本節提供實作 AccountProfile
和 SubscriptionEntity
的必要程式碼範例和操作說明,以便管理各種訂閱項目類型。
使用者帳戶和個人資料
如要在 Google TV 上使用個人化功能,請提供帳戶資訊。使用 AccountProfile
提供:
- 帳戶 ID:代表使用者帳戶的專屬 ID。這可以是實際帳戶 ID,或是經過適當模糊處理的版本。
// Set the account ID to which the subscription applies.
// Don't set the profile ID because subscription applies to account level.
val accountProfile = AccountProfile.Builder()
.setAccountId("user_account_id")
.setProfileId("user_profile id")
.build();
一般級別訂閱
如果使用者訂閱媒體供應商服務的基本方案 (例如,訂閱等級為 1,可存取所有付費內容),請提供下列必要詳細資料:
訂閱類型:明確指出使用者訂閱的特定方案。
SUBSCRIPTION_TYPE_ACTIVE
:使用者已訂閱付費方案。SUBSCRIPTION_TYPE_ACTIVE_TRIAL
:使用者有試用訂閱方案。SUBSCRIPTION_TYPE_INACTIVE
:使用者擁有帳戶,但沒有有效的訂閱或試用方案。
到期時間:可選的時間,以毫秒為單位。指定訂閱項目的到期時間。
提供者套件名稱:指定處理訂閱項目的應用程式套件名稱。
範例:媒體供應商動態饋給示例。
"actionAccessibilityRequirement": [
{
"@type": "ActionAccessSpecification",
"category": "subscription",
"availabilityStarts": "2022-06-01T07:00:00Z",
"availabilityEnds": "2026-05-31T07:00:00Z",
"requiresSubscription": {
"@type": "MediaSubscription",
// Don't match this string,
// ID is only used to for reconciliation purpose
"@id": "https://www.example.com/971bfc78-d13a-4419",
// Don't match this, as name is only used for displaying purpose
"name": "Basic common name",
"commonTier": true
}
以下範例會為使用者建立 SubscriptionEntity
:
val subscription = SubscriptionEntity
.Builder()
setSubscriptionType(
SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
)
.setProviderPackageName("com.google.android.example")
// Optional
// December 30, 2025 12:00:00AM in milliseconds since epoch
.setExpirationTimeMillis(1767052800000)
.build();
Premium 訂閱
如果應用程式提供多層次的進階訂閱方案,其中包含超出一般層次的擴充內容或功能,請在 Subscription 中新增一或多個授權,以表示這項情況。
此授權包含下列欄位:
- ID:此授權的必要 ID 字串。這項資訊必須與媒體供應商發布至 Google TV 的動態饋給中提供的授權 ID 相符 (請注意,這不是 ID 欄位)。
- 名稱:這是輔助資訊,用於權限比對。雖然這是選用項目,但提供人類可讀的授權名稱,有助於開發人員和支援團隊瞭解使用者授權。例如:Sling Orange。
- Expiration TimeMillis:如果此授權的到期時間與訂閱到期時間不同,您可以選擇以毫秒為單位指定到期時間。根據預設,授權會隨著訂閱方案到期而失效。
以下是媒體供應器動態饋給範例片段:
"actionAccessibilityRequirement": [
{
"@type": "ActionAccessSpecification",
"category": "subscription",
"availabilityStarts": "2022-06-01T07:00:00Z",
"availabilityEnds": "2026-05-31T07:00:00Z",
"requiresSubscription": {
"@type": "MediaSubscription",
// Don't match this string,
// ID is only used to for reconciliation purpose
"@id": "https://www.example.com/971bfc78-d13a-4419",
// Don't match this, as name is only used for displaying purpose
"name": "Example entitlement name",
"commonTier": false,
// match this identifier in your API. This is the crucial
// entitlement identifier used for recommendation purpose.
"identifier": "example.com:entitlementString1"
}
以下範例會為訂閱的使用者建立 SubscriptionEntity
:
// Subscription with entitlements.
// The entitlement expires at the same time as its subscription.
val subscription = SubscriptionEntity
.Builder()
.setSubscriptionType(
SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
)
.setProviderPackageName("com.google.android.example")
// Optional
// December 30, 2025 12:00:00AM in milliseconds
.setExpirationTimeMillis(1767052800000)
.addEntitlement(
SubscriptionEntitlement.Builder()
// matches with the identifier in media provider feed
.setEntitlementId("example.com:entitlementString1")
.setDisplayName("entitlement name1")
.build()
)
.build();
// Subscription with entitlements
// The entitement has different expiration time from its subscription
val subscription = SubscriptionEntity
.Builder()
.setSubscriptionType(
SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
)
.setProviderPackageName("com.google.android.example")
// Optional
// December 30, 2025 12:00:00AM in milliseconds
.setExpirationTimeMillis(1767052800000)
.addEntitlement(
SubscriptionEntitlement.Builder()
.setEntitlementId("example.com:entitlementString1")
.setDisplayName("entitlement name1")
// You may set the expiration time for entitlement
// December 15, 2025 10:00:00 AM in milliseconds
.setExpirationTimeMillis(1765792800000)
.build())
.build();
已連結服務套裝方案的訂閱項目
雖然訂閱項目通常屬於原始應用程式的媒體供應商,但您可以在訂閱項目中指定連結的服務套件名稱,將訂閱項目歸屬於連結的服務套件。
以下程式碼範例說明如何建立使用者訂閱項目。
// Subscription for linked service package
val subscription = SubscriptionEntity
.Builder()
.setSubscriptionType(
SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
)
.setProviderPackageName("com.google.android.example")
// Optional
// December 30, 2025 12:00:00AM in milliseconds since epoch
.setExpirationTimeMillis(1767052800000)
.build();
此外,如果使用者訂閱了其他附屬服務,請新增其他訂閱項目,並據此設定連結的服務套件名稱。
// Subscription for linked service package
val linkedSubscription = Subscription
.Builder()
.setSubscriptionType(
SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
)
.setProviderPackageName("linked service package name")
// Optional
// December 30, 2025 12:00:00AM in milliseconds since epoch
.setExpirationTimeMillis(1767052800000)
.addBundledSubscription(
BundledSubscription.Builder()
.setBundledSubscriptionProviderPackageName(
"bundled-subscription-package-name"
)
.setSubscriptionType(SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE)
.setExpirationTimeMillis(111)
.addEntitlement(
SubscriptionEntitlement.Builder()
.setExpirationTimeMillis(111)
.setDisplayName("Silver subscription")
.setEntitlementId("subscription.tier.platinum")
.build()
)
.build()
)
.build();
視需要,也可以為已連結的服務訂閱項目新增授權。
提供訂閱組合
在應用程式於前景運作時執行內容發布工作。
使用 AppEngagePublishClient
類別的 publishSubscriptionCluster()
方法發布 SubscriptionCluster
物件。
使用 isServiceAvailable
檢查服務是否可供整合。
client.publishSubscription(
PublishSubscriptionRequest.Builder()
.setAccountProfile(accountProfile)
.setSubscription(subscription)
.build();
)
使用 setSubscription()
驗證使用者應只訂閱一項服務。
使用 addLinkedSubscription()
或 addLinkedSubscriptions()
(可接受連結訂閱項目清單),讓使用者可以擁有零或多個連結訂閱項目。
服務收到要求後,系統會建立新項目,並在 60 天後自動刪除舊項目。系統一律會使用最新的項目。如果發生錯誤,整個要求都會遭到拒絕,現有狀態則維持不變。
保持訂閱項目最新
- 如要立即提供變更內容,請在使用者的訂閱狀態發生變更時 (例如啟用、停用、升級、降級) 呼叫
publishSubscriptionCluster()
。 如要定期驗證持續的準確性,請每月至少呼叫一次
publishSubscriptionCluster()
。如要刪除影片探索資料,請在標準 60 天保留期限前,使用
client.deleteClusters()
方法從 Google TV 伺服器手動刪除使用者資料。這會刪除帳戶個人資料或整個帳戶的所有現有影片探索資料,具體取決於指定的DeleteReason
。移除使用者訂閱的程式碼片段
// If the user logs out from your media app, you must make the following call // to remove subscription and other video discovery data from the current // google TV device. client.deleteClusters( new DeleteClustersRequest.Builder() .setAccountProfile( AccountProfile .Builder() .setAccountId() .setProfileId() .build() ) .setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT) .build() ) ``` Following code snippet demonstrates removal of user subscription when user revokes the consent. ```Kotlin // If the user revokes the consent to share across device, make the call // to remove subscription and other video discovery data from all google // TV devices. client.deleteClusters( new DeleteClustersRequest.Builder() .setAccountProfile( AccountProfile .Builder() .setAccountId() .setProfileId() .build() ) .setReason(DeleteReason.DELETE_REASON_LOSS_OF_CONSENT) .build() ) ``` Following code demonstrates how to remove subscription data on user profile deletion. ```Kotlin // If the user delete a specific profile, you must make the following call // to remove subscription data and other video discovery data. client.deleteClusters( new DeleteClustersRequest.Builder() .setAccountProfile( AccountProfile .Builder() .setAccountId() .setProfileId() .build() ) .setReason(DeleteReason.DELETE_REASON_ACCOUNT_PROFILE_DELETION) .build() )
測試
本節將逐步說明如何測試訂閱項目的實作方式。請先驗證資料準確性和功能正確無誤,再推出應用程式。
發布整合功能檢查清單
應用程式處於前景且使用者積極與其互動時,應進行發布作業。
發布時機:
- 使用者首次登入。
- 使用者變更設定檔 (如果系統支援設定檔)。
- 使用者購買新的訂閱項目。
- 使用者升級訂閱。
- 使用者訂閱項目到期。
檢查應用程式是否正確呼叫發布事件的 logcat 中的
isServiceAvailable()
和publishClusters()
API。確認驗證應用程式可顯示資料。驗證應用程式應將訂閱項目顯示為獨立的一列。叫用發布 API 時,資料應會顯示在驗證應用程式中。
前往應用程式並執行下列各項操作:
- 登入。
- 切換設定檔 (如有支援)。
- 購買新的訂閱項目。
- 升級現有訂閱項目。
- 讓訂閱項目到期。
驗證整合
如要測試整合作業,請使用驗證應用程式。
驗證應用程式是 Android 應用程式,可讓開發人員用來驗證整合作業是否有效。此應用程式內含可協助開發人員驗證資料和廣播意圖的功能。這有助於在發布前驗證資料準確性和功能是否正常運作。
- 針對每個事件,檢查應用程式是否已叫用
publishSubscription
API。在驗證應用程式中驗證已發布的資料。 確認驗證應用程式中的所有項目皆為綠色 如果所有實體資訊都正確,系統會在所有實體中顯示「All Good」綠色勾號。
圖 1. 成功訂閱 驗證應用程式也會標示問題
圖 2.訂閱失敗 如要查看套裝訂閱項目中的問題,請使用電視遙控器將焦點放在特定套裝訂閱項目,然後點選即可查看問題。你可能需要先將焦點放在該列,然後向右移動,才能找到「套裝訂閱」資訊卡。如圖 3 所示,問題會以紅色標示。此外,請使用遙控器向下移動,查看套裝訂閱方案中授權的問題
圖 3.訂閱錯誤 如要查看授權問題,請使用電視遙控器將焦點放在特定授權上,然後點選即可查看問題。問題會以紅色標示。
圖 4.訂閱錯誤詳細資料