Engage SDK を使用してアプリの利用資格を Google TV と共有する

このガイドでは、Engage SDK を使用してアプリの定期購入データと利用資格データを Google TV と共有する手順について説明します。ユーザーは、利用資格のあるコンテンツを見つけ、テレビ、モバイル、タブレットの Google TV エクスペリエンス内で、Google TV がユーザーに関連性の高いコンテンツをおすすめできるようにできます。

前提条件

Device Entitlement API を使用するには、メディア アクション フィードのオンボーディングが必要です。まだ行っていない場合は、メディア アクション フィードのオンボーディング プロセスを完了します。

事前作業

始める前に、次の手順を完了します。この統合でアプリが API レベル 19 以降をターゲットにしていることを確認します。

  1. アプリに com.google.android.engage ライブラリを追加します。

    統合に使用する SDK は、モバイルアプリ用とテレビアプリ用の 2 つあります。

    モバイルの場合

    
      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 サービス環境を本番環境に設定します。

    モバイル 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>
    
  3. APK を Google に送信する前に、AndroidManifest.xml ファイルでエンゲージ サービスの環境を本番環境に設定します。パフォーマンスを最適化し、将来の互換性を維持するには、アプリがフォアグラウンドにあり、ユーザーがアプリをアクティブに操作しているとき(アプリの起動時、ログイン後、アクティブな使用中など)にのみデータを公開します。バックグラウンド プロセスからの公開は推奨されません。

  4. 次のイベントで定期購入情報を公開します。

    1. ユーザーがアプリにログインします。
    2. ユーザーがプロファイルを切り替える(プロファイルがサポートされている場合)。
    3. ユーザーが新しい定期購入を購入する。
    4. お客様が既存の定期購入をアップグレードする。
    5. ユーザーの定期購入が期限切れになる。

統合

このセクションでは、さまざまなサブスクリプション タイプを管理するために AccountProfileSubscriptionEntity を実装するために必要なコードサンプルと手順について説明します。

ユーザー アカウントとプロフィール

Google TV でパーソナライズされた機能を利用できるようにするには、アカウント情報を提供してください。AccountProfile を使用して、次の情報を指定します。

  1. アカウント 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 つの定期購入階層があるサービスなど)には、次の重要な情報を提供します。

  1. 定期購入の種類: お客様がご利用の定期購入プランを明記します。

    1. SUBSCRIPTION_TYPE_ACTIVE: ユーザーが有効な有料サブスクリプションを持っている。
    2. SUBSCRIPTION_TYPE_ACTIVE_TRIAL: ユーザーがトライアル サブスクリプションを利用している。
    3. SUBSCRIPTION_TYPE_INACTIVE: ユーザーにアカウントはあるが、有効な定期購入またはトライアルはない。
  2. 有効期限: 省略可能な時間(ミリ秒単位)。定期購入の有効期限を指定します。

  3. プロバイダのパッケージ名: 定期購入を処理するアプリのパッケージ名を指定します。

サンプル メディア プロバイダ フィードの例。

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

プレミアム定期購入

アプリで複数階層のプレミアム サブスクリプション パッケージ(一般的な階層を超える拡張コンテンツや機能を含む)を提供している場合は、Subscription に 1 つ以上の利用資格を追加して、これを表します。

この利用資格には次のフィールドがあります。

  1. 識別子: この利用資格に必要な識別子文字列。これは、Google TV に公開されたメディア プロバイダのフィードで提供される利用資格 ID(ID フィールドではない)のいずれかに一致する必要があります。
  2. 名前: 補助情報で、利用資格の照合に使用されます。人間が判読できる利用資格名を指定することは任意ですが、指定すると、デベロッパーとサポートチームの両方がユーザーの利用資格を把握しやすくなります。例: Sling Orange。
  3. 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() を使用して、ユーザーがサービスにサブスクリプションを 1 つだけ持つ必要があることを確認します。

addLinkedSubscription() またはリンクされたサブスクリプションのリストを受け入れることができる addLinkedSubscriptions() を使用して、ユーザーが 0 個以上のリンクされたサブスクリプションを持てるようにします。

サービスがリクエストを受信すると、新しいエントリが作成され、古いエントリは 60 日後に自動的に削除されます。システムは常に最新のエントリを使用します。エラーが発生した場合は、リクエスト全体が拒否され、それまでの状態が維持されます。

サブスクリプションを最新の状態に保つ

  1. 変更時に即時更新を提供するには、有効化、無効化、アップグレード、ダウングレードなど、ユーザーの定期購入の状態が変更されるたびに publishSubscriptionCluster() を呼び出します。
  2. 継続的な精度を確保するために、少なくとも月に 1 回 publishSubscriptionCluster() を呼び出します。

  3. 動画検出データを削除するには、標準の 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()
    )
    

テスト

このセクションでは、定期購入の実装をテストする手順について説明します。リリース前にデータの精度と適切な機能を確認します。

統合の公開チェックリスト

  1. 公開は、アプリがフォアグラウンドにあり、ユーザーがアプリをアクティブに操作しているときに行われます。

  2. 公開するタイミング:

    • ユーザーが初めてログインしたとき。
    • ユーザーがプロファイルを変更する(プロファイルがサポートされている場合)。
    • お客様が新しい定期購入を購入します。
    • ユーザーがサブスクリプションをアップグレードします。
    • ユーザーの定期購入が期限切れになる。
  3. アプリが、公開イベントで logcat で isServiceAvailable() API と publishClusters() API を正しく呼び出しているかどうかを確認します。

  4. 検証用アプリにデータが表示されていることを確認します。検証用アプリでは、サブスクリプションが別々の行として表示されます。publish API が呼び出されると、データが検証用アプリに表示されます。

    • アプリの Android マニフェスト ファイルで、Engage Service Flag が本番環境に設定されていないことを確認します。
    • Engage Verification アプリをインストールして開きます。
    • 確認アプリで isServiceAvailable の値が false の場合は、確認アプリ内の Toggle ボタンをクリックして true に設定します。
    • アプリのパッケージ名を入力すると、公開されたデータが自動的に表示されます。
  5. アプリに移動し、次の操作をそれぞれ行います。

    • ログインします。
    • プロファイルを切り替える(サポートされている場合)。
    • 新しい定期購入を購入する。
    • 既存の定期購入をアップグレードする。
    • サブスクリプションを期限切れにする。

統合を確認する

統合をテストするには、検証アプリを使用します。

検証用アプリは、デベロッパーが統合が適切に機能しているか確認するための Android アプリです。このアプリには、データやブロードキャスト インテントを検証する際に役立つ機能が含まれています。リリース前にデータの精度と適切な機能を検証するのに役立ちます。

  1. 各イベントについて、アプリが publishSubscription API を呼び出しているかどうかを確認します。検証用アプリでパブリッシュされたデータを確認します。検証用アプリですべてが緑色になっていることを確認します
  2. すべてのエンティティの情報が正しい場合は、すべてのエンティティに「問題なし」の緑色のチェックマークが表示されます。

    検証アプリの成功スクリーンショット
    図 1. 定期購入が完了しました
  3. 問題は確認アプリでもハイライト表示されます

    検証アプリのエラーのスクリーンショット
    図 2. 定期購入に失敗した場合
  4. バンドル サブスクリプションの問題を確認するには、テレビのリモコンを使用してそのバンドル サブスクリプションに移動し、クリックして問題を表示します。バンドル サブスクリプション カードを見つけるには、まず行にフォーカスを合わせて右に移動する必要があります。問題は図 3 のように赤色でハイライト表示されます。また、リモコンを使用して下に移動すると、バンドル サブスクリプション内の利用資格の問題を確認できます。

    検証アプリのエラーの詳細のスクリーンショット
    図 3.定期購入エラー
  5. 利用資格の問題を確認するには、テレビのリモコンを使用してその特定の利用資格に移動し、クリックして問題を確認します。問題は赤色でハイライト表示されます。

    検証アプリのエラーのスクリーンショット
    図 4.サブスクリプション エラーの詳細