book_path: /distribute/other-docs/_book.yaml project_path: /distribute/other-docs/_project.yaml
В этом руководстве содержатся инструкции для разработчиков по обмену данными о подписках и правах доступа к приложениям с Google TV с помощью Engage SDK . Пользователи могут находить контент, на который у них есть право, и настраивать Google TV на предоставление пользователям наиболее релевантных рекомендаций по контенту непосредственно в приложениях Google TV на телевизорах, мобильных устройствах и планшетах.
Предварительные требования
Для использования API управления правами доступа к устройствам необходимо предварительно подключить ленту действий с медиаконтентом. Если вы еще этого не сделали, завершите процесс подключения ленты действий с медиаконтентом .
Предварительная работа
Выполните предварительные инструкции в руководстве «Начало работы» .
- Опубликуйте информацию о подписке на следующие мероприятия:
- Пользователь входит в ваше приложение.
- Пользователь переключается между профилями (если профили поддерживаются).
- Пользователь приобретает новую подписку.
- Пользователь обновляет существующую подписку.
- Срок действия подписки пользователя истекает.
Интеграция
В этом разделе представлены необходимые примеры кода и инструкции по реализации SubscriptionEntity для управления различными типами подписок.
Подписка стандартного уровня
Для пользователей с базовой подпиской на услуги медиапровайдера, например, на сервис с одним уровнем подписки, предоставляющим доступ ко всему платному контенту, необходимо предоставить следующие основные данные:
SubscriptionType: Четко укажите конкретный тарифный план подписки пользователя.-
SUBSCRIPTION_TYPE_ACTIVE: У пользователя есть активная платная подписка. -
SUBSCRIPTION_TYPE_ACTIVE_TRIAL: У пользователя есть пробная подписка. -
SUBSCRIPTION_TYPE_INACTIVE: У пользователя есть учетная запись, но нет активной подписки или пробного периода.
-
ExpirationTimeMillis: Необязательный параметр времени в миллисекундах. Укажите, когда должна истечь подписка.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()
Премиум-подписка
Если приложение предлагает многоуровневые премиум-подписки, включающие расширенный контент или функции, выходящие за рамки стандартного уровня, отразите это, добавив одно или несколько прав доступа к подписке.
Данная лицензия содержит следующие поля:
-
Identifier: Обязательный строковый идентификатор для данного права доступа. Он должен совпадать с одним из идентификаторов права доступа (обратите внимание, что это не поле ID), указанных в ленте медиапровайдера, опубликованной на Google TV. -
Name: Это вспомогательная информация, используемая для сопоставления прав доступа. Хотя это необязательно, указание удобочитаемого названия права доступа улучшает понимание прав доступа пользователей как разработчиками, так и службами поддержки. Например: Sling Orange. -
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 дней. Система всегда использует самую последнюю запись. В случае ошибки весь запрос отклоняется, и сохраняется текущее состояние.
Поддерживайте подписку в актуальном состоянии.
Для обеспечения мгновенного обновления информации при изменениях, вызывайте
publishSubscriptionClusterвсякий раз, когда изменяется состояние подписки пользователя, например, активация, деактивация, обновление или понижение уровня подписки.Для обеспечения регулярной проверки и поддержания точности данных, вызывайте
publishSubscriptionClusterне реже одного раза в месяц.Чтобы удалить данные о поиске видео, вручную удалите данные пользователя с сервера 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() )
Тестирование
В этом разделе представлено пошаговое руководство по тестированию реализации подписки. Перед запуском проверьте точность данных и корректность работы системы.
Контрольный список интеграции публикации
Публикация должна происходить, когда приложение находится на переднем плане и пользователь активно с ним взаимодействует.
Когда будет опубликовано:
- Пользователь впервые входит в систему.
- Пользователь изменяет профиль (если профили поддерживаются).
- Пользователь приобретает новую подписку.
- Пользователь обновляет подписку.
- Срок действия подписки пользователя истекает.
Проверьте в logcat, корректно ли приложение вызывает API-функции
isServiceAvailable()иpublishClusters()при возникновении событий публикации.Убедитесь, что данные отображаются в приложении для подтверждения. Приложение для подтверждения должно отображать подписку в отдельной строке. При вызове API публикации данные должны отобразиться в приложении для подтверждения.
Перейдите в приложение и выполните каждое из следующих действий:
- Войти.
- Переключение между профилями (если поддерживается).
- Оформите новую подписку.
- Обновите существующую подписку.
- Отмените подписку.
Проверьте интеграцию
Для проверки интеграции используйте приложение для проверки .
- Для каждого события проверьте, вызвало ли приложение API
publishSubscription. Проверьте опубликованные данные в приложении для проверки. Убедитесь, что в приложении для проверки все отображается зеленым цветом. Если вся информация об объекте верна, во всех объектах отображается зеленая галочка «Все в порядке».

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

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

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

Рисунок 4. Подробности ошибки подписки.