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

book_path: /distribute/other-docs/_book.yaml project_path: /distribute/other-docs/_project.yaml

このガイドでは、デベロッパーが Engage SDK を使用してアプリの定期購入と利用資格のデータを Google TV と共有する手順について説明します。ユーザーは、利用できるコンテンツを見つけて Google TV を有効にすることで、テレビ、モバイル、タブレットの Google TV エクスペリエンス内で、ユーザーに非常に適したコンテンツのレコメンデーションを Google TV から直接受け取ることができます。

前提条件

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

事前作業

スタートガイドの事前準備の手順を完了していること。

  1. 次のイベントでサブスクリプション情報を公開します。
    1. ユーザーがアプリにログインします。
    2. ユーザーがプロファイルを切り替えます(プロファイルがサポートされている場合)。
    3. ユーザーが新しい定期購入を購入します。
    4. ユーザーが既存の定期購入をアップグレードする。
    5. ユーザーの定期購入の有効期限が切れます。

統合

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

共通の定期購入

メディア プロバイダ サービスの基本サブスクリプション(すべての有料コンテンツへのアクセス権を付与する 1 つのサブスクリプション ティアがあるサービスなど)をご利用のユーザー向けに、次の重要な詳細情報を提供します。

  1. SubscriptionType: ユーザーが利用している特定のサブスクリプション プランを明確に示します。

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

  3. ProviderPackageName: サブスクリプションを処理するアプリのパッケージ名を指定します。

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

"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. Identifier: この利用資格に必要な識別子文字列。これは、Google TV に公開されたメディア プロバイダのフィードで提供される利用資格 ID のいずれかと一致している必要があります(ID フィールドではないことに注意してください)。
  2. Name: これは補助情報であり、利用資格の照合に使用されます。省略可能ですが、人が読める利用資格名を提供すると、デベロッパーとサポートチームの両方でユーザーの利用資格を理解しやすくなります。例: Sling Orange。
  3. ExpirationTimeMillis: サブスクリプションの有効期限と異なる場合は、この利用資格の有効期限をミリ秒単位で指定します(省略可)。デフォルトでは、利用資格はサブスクリプションの有効期限とともに期限切れになります。

次のサンプル メディア プロバイダ フィード スニペットの場合:

"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 オブジェクトを公開します。

スタートガイドの説明に沿って、クライアントを初期化し、サービスの可用性を確認してください。

client.publishSubscription(
  PublishSubscriptionRequest.Builder()
    .setAccountProfile(accountProfile)
    .setSubscription(subscription)
    .build()
  )

setSubscription() を使用して、ユーザーがサービスへのサブスクリプションを 1 つだけ持つようにします。

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

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

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

  1. 変更時に即座に更新を提供するには、ユーザーの定期購入の状態(有効化、無効化、アップグレード、ダウングレードなど)が変更されるたびに publishSubscriptionCluster を呼び出します。

  2. 継続的な精度を定期的に検証するには、少なくとも月に 1 回 publishSubscriptionCluster を呼び出します。

  3. 動画検出データを削除するには、標準の 60 日間の保持期間が経過する前に、Google TV サーバーからユーザーのデータを手動で削除します。この場合は、client.deleteClusters メソッドを使用します。これにより、指定された 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)
      .setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
      .build()
      )
    

    次のコード スニペットは、ユーザーが同意を取り消したときにユーザーの定期購入を削除する方法を示しています。

    // 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)
        .setReason(DeleteReason.DELETE_REASON_LOSS_OF_CONSENT)
        .build()
    )
    

    次のコードは、ユーザー プロファイルの削除時に定期購入データを削除する方法を示しています。

    // 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)
      .setReason(DeleteReason.DELETE_REASON_ACCOUNT_PROFILE_DELETION)
      .build()
    )
    

テスト

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

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

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

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

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

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

  5. アプリに移動し、次の操作を行います。

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

統合を検証する

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

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

    Verification App Success Screenshot
    図 1. サブスクリプションの登録が完了しました
  3. 問題は確認アプリでもハイライト表示されます

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

    Verification App Error Details Screenshot(検証アプリのエラー詳細のスクリーンショット)
    図 3.サブスクリプション エラー
  5. 利用資格の問題を確認するには、テレビのリモコンを使用してその利用資格にフォーカスし、クリックして問題を表示します。問題は赤色でハイライト表示されます。

    確認アプリのエラーのスクリーンショット
    図 4.定期購入エラーの詳細