Как передавать данные о правах на контент в Google TV с помощью Engage SDK

В этом руководстве рассказывается, как разработчики могут передавать в Google TV данные о подписках и правах доступа в приложении с помощью Engage SDK. Пользователи могут находить контент, к которому у них есть доступ, и получать от Google TV рекомендации, основанные на их предпочтениях, прямо в интерфейсе Google TV на телевизоре, мобильном устройстве или планшете.

Требования

Прежде чем использовать API Device Entitlement, необходимо добавить фид действий с медиаконтентом. Если вы ещё не сделали этого, завершите процесс регистрации фида действий с медиаконтентом.

Подготовка

Выполните инструкции из раздела Предварительная работа в руководстве по началу работы.

  1. Публиковать информацию о подписке при следующих событиях:
    1. Пользователь входит в ваше приложение.
    2. Пользователь переключается между профилями (если профили поддерживаются).
    3. Пользователь оформляет новую подписку.
    4. Пользователь обновляет существующую подписку.
    5. Срок действия подписки пользователя истекает.

Интеграция

В этом разделе приведены примеры кода и инструкции по реализации SubscriptionEntity для управления разными типами подписок.

Подписка Common tier

Для пользователей с базовыми подписками на сервисы поставщиков медиаконтента, например на сервис с одним уровнем подписки, который предоставляет доступ ко всему платному контенту, укажите следующие основные сведения:

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

Подписка Premium

Если в приложении есть несколько уровней подписки Premium, которые включают расширенный контент или функции, добавьте в Subscription одно или несколько разрешений.

У этого права есть следующие поля:

  1. Identifier – обязательная строка идентификатора для этого права. Он должен совпадать с одним из идентификаторов прав (обратите внимание, что это не поле ID), указанных в фиде поставщика контента, который опубликован в Google TV.
  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 = SubscriptionEntity.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()

Вы также можете добавить права доступа к подписке на связанный сервис.

Укажите набор подписок

Запустите задание публикации контента, пока приложение открыто.

Чтобы опубликовать объект SubscriptionCluster, используйте метод publishSubscriptionCluster() из класса AppEngagePublishClient.

Инициализируйте клиент и проверьте доступность сервиса, как описано в руководстве по началу работы.

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

Используйте setSubscription(), чтобы убедиться, что у пользователя только одна подписка на сервис.

Используйте addLinkedSubscription() или addLinkedSubscriptions(), которые принимают список связанных подписок, чтобы у пользователя могло быть несколько связанных подписок или не быть ни одной.

Когда сервис получает запрос, создается новая запись, а старая автоматически удаляется через 60 дней. Система всегда использует последнюю запись. В случае ошибки весь запрос отклоняется и сохраняется существующее состояние.

Как поддерживать актуальность подписки

  1. Чтобы сразу же получать обновления при изменениях, вызывайте publishSubscriptionCluster каждый раз, когда меняется статус подписки пользователя, например при активации, деактивации, повышении или понижении уровня.

  2. Чтобы регулярно проверять точность данных, вызывайте publishSubscriptionCluster не реже одного раза в месяц.

  3. Чтобы вручную удалить данные пользователя с сервера Google TV до истечения стандартного 60-дневного срока хранения, используйте метод client.deleteClusters. При этом удаляются все существующие данные Engage для профиля аккаунта или для всего аккаунта в зависимости от указанного значения DeleteReason.

    В следующем фрагменте кода показано, как удалить подписку пользователя:

    // If the user logs out from your media app, you must make the following call
    // to remove subscription and other Engage data from the current
    // google TV device.
    client.deleteClusters(
      DeleteClustersRequest.Builder()
        .setAccountProfile(accountProfile)
        .setDeleteReason(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 Engage data from all google
    // TV devices.
    client.deleteClusters(
      DeleteClustersRequest.Builder()
        .setAccountProfile(accountProfile)
        .setDeleteReason(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 Engage data.
    client.deleteClusters(
      DeleteClustersRequest.Builder()
        .setAccountProfile(accountProfile)
        .setDeleteReason(DeleteReason.DELETE_REASON_ACCOUNT_PROFILE_DELETION)
        .build()
    )
    

Тестирование

В этом разделе приведены пошаговые инструкции по тестированию подписок. Перед запуском проверьте точность данных и правильность работы.

Контрольный список интеграции для публикации

  1. Публикация должна происходить, когда приложение находится на переднем плане и пользователь активно взаимодействует с ним.

  2. Когда публиковать:

    • Пользователь входит в аккаунт в первый раз.
    • Пользователь меняет профиль (если профили поддерживаются).
    • Пользователь оформляет новую подписку.
    • Пользователь переходит на более дорогую подписку.
    • Срок действия подписки пользователя истекает.
  3. Проверьте, правильно ли приложение вызывает API isServiceAvailable и publishClusters в logcat при публикации событий.

  4. Убедитесь, что данные видны в приложении для проверки. Подписка должна отображаться в отдельной строке. При вызове API публикации данные должны появиться в приложении для проверки.

  5. Перейдите в приложение и выполните следующие действия:

    • Войдите в аккаунт.
    • Переключаться между профилями (если поддерживается).
    • Оформите новую подписку.
    • Перейти на более высокую версию.
    • Срок действия подписки истек.

Проверьте интеграцию

Чтобы протестировать интеграцию, используйте приложение для проверки.

  1. Для каждого события проверьте, вызывало ли приложение API publishSubscription. Проверьте опубликованные данные в приложении для проверки. Убедитесь, что в приложении для проверки все индикаторы зеленые.
  2. Если вся информация об объекте верна, то для всех объектов будет показан зеленый флажок "Все в порядке".

    Скриншот, подтверждающий успешную проверку приложения
    Рисунок 1. Подписка оформлена
  3. Проблемы также выделены в приложении для проверки.

    Скриншот ошибки в приложении для проверки
    Рисунок 2. Ошибка при оформлении подписки.
  4. Чтобы посмотреть проблемы с пакетной подпиской, выберите ее с помощью пульта телевизора и нажмите на нее. Возможно, сначала вам потребуется перейти к строке, а затем вправо, чтобы найти карточку "Пакетная подписка". Проблемы выделены красным цветом, как показано на рис. 3. Также с помощью пульта прокрутите вниз, чтобы посмотреть проблемы с правами в пакетной подписке.

    Скриншот с подробными сведениями об ошибке в приложении для проверки
    Рисунок 3. Ошибки подписки
  5. Чтобы посмотреть проблемы с правом, выберите его с помощью пульта телевизора и нажмите на него. Проблемы выделены красным цветом.

    Скриншот ошибки в приложении для проверки
    Рисунок 4. Сведения об ошибке подписки