Поделиться правами на приложения с Google TV с помощью Engage SDK

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

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

Предварительные требования

Для использования API управления правами доступа к устройствам необходимо предварительно подключить ленту действий с медиаконтентом. Если вы еще этого не сделали, завершите процесс подключения ленты действий с медиаконтентом .

Предварительная работа

Выполните предварительные инструкции в руководстве «Начало работы» .

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

Интеграция

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

Подписка стандартного уровня

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

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

Премиум-подписка

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

Данная лицензия содержит следующие поля:

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

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

Предоставить набор подписок

Запустите задачу публикации контента, когда приложение находится в фоновом режиме.

Для публикации объекта 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 . Это удалит все существующие данные о поиске видео для профиля учетной записи или для всей учетной записи в зависимости от указанной 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. Проверьте в logcat, корректно ли приложение вызывает API-функции isServiceAvailable() и publishClusters() при возникновении событий публикации.

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

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

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

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

Для проверки интеграции используйте приложение для проверки .

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

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

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

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

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