Continue Watching использует кластер Continuation для показа незаконченных видео и следующих серий для просмотра из того же сезона ТВ из нескольких приложений в одной группе пользовательского интерфейса. Вы можете включить их сущности в этот кластер продолжения. Следуйте этому руководству, чтобы узнать, как улучшить взаимодействие пользователей с помощью Continue Watching с помощью Engage SDK .
Шаг 1: Предварительная работа
Прежде чем начать, выполните следующие шаги:
Убедитесь, что ваше приложение ориентировано на API уровня 19 или выше для этой интеграции.
Добавьте библиотеку
com.google.android.engage
в свое приложение:Для интеграции используются отдельные SDK: один для мобильных приложений и один для ТВ-приложений.
Мобильный
dependencies { implementation 'com.google.android.engage:engage-core:1.5.5 }
ТВ
dependencies { implementation 'com.google.android.engage:engage-tv:1.0.2 }
Настройте среду службы Engage на работу в файле
AndroidManifest.xml
.Мобильный
<meta-data android:name="com.google.android.engage.service.ENV" android:value="PRODUCTION"> </meta-data>
ТВ
<meta-data android:name="com.google.android.engage.service.ENV" android:value="PRODUCTION"> </meta-data>
Добавить разрешение для
WRITE_EPG_DATA
для tv apk<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
Обеспечьте надежную публикацию контента, используя для планирования фоновую службу, например
androidx.work
.Чтобы обеспечить бесперебойный просмотр, публикуйте данные о продолжении просмотра при возникновении следующих событий:
- Первый вход в систему: когда пользователь входит в систему в первый раз, публикация его данных гарантирует немедленный доступ к истории просмотров.
- Создание или переключение профилей (приложения с несколькими профилями): если ваше приложение поддерживает несколько профилей, публикуйте данные, когда пользователь создает или переключает профили. Это гарантирует каждому пользователю персонализированный опыт.
- Прерывание воспроизведения видео: чтобы помочь пользователям продолжить с того места, где они остановились, публикуйте данные, когда они приостанавливают или останавливают видео, или когда приложение закрывается во время воспроизведения.
- Обновления лотка Continue Watching (если поддерживается): когда пользователь удаляет элемент из лотка Continue Watching, отразите это изменение, опубликовав обновленные данные. Это гарантирует, что лоток останется актуальным и персонализированным.
- Завершение видео:
- Для фильмов удалите завершенный фильм из лотка «Продолжить просмотр». Если фильм является частью серии, добавьте следующий фильм, чтобы удержать внимание пользователя.
- Для эпизодов удалите завершенный эпизод и добавьте следующий эпизод серии, если он доступен, чтобы побудить зрителей продолжать просмотр.
Интеграция
Профиль учетной записи
Чтобы разрешить персонализированный опыт «продолжить просмотр» на Google TV, предоставьте информацию об учетной записи и профиле. Используйте AccountProfile, чтобы предоставить:
ID учетной записи: уникальный идентификатор, представляющий учетную запись пользователя в вашем приложении. Это может быть фактический ID учетной записи или его соответствующим образом замаскированная версия.
Идентификатор профиля (необязательно): если ваше приложение поддерживает несколько профилей в рамках одной учетной записи, укажите уникальный идентификатор для конкретного профиля пользователя (опять же, реальный или замаскированный).
// If your app only supports account
val accountProfile = AccountProfile.Builder()
.setAccountId("your_users_account_id")
.build()
// If your app supports both account and profile
val accountProfile = AccountProfile.Builder()
.setAccountId("your_users_account_id")
.setProfileId("your_users_profile_id")
.build()
Создать сущности
SDK определил различные сущности для представления каждого типа элемента. Кластер продолжения поддерживает следующие сущности:
Укажите специфичные для платформы URI и изображения постеров для этих сущностей.
Также создайте URI воспроизведения для каждой платформы, например Android TV, Android или iOS, если вы этого еще не сделали. Таким образом, когда пользователь продолжает просмотр на каждой платформе, приложение использует целевой URI воспроизведения для воспроизведения видеоконтента.
// Required. Set this when you want continue watching entities to show up on
// Google TV
val playbackUriTv =
PlatformSpecificUri.Builder()
.setPlatformType(PlatformType.TYPE_ANDROID_TV)
.setActionUri(Uri.parse("https://www.example.com/entity_uri_for_tv"))
.build()
// Required. Set this when you want continue watching entities to show up on
// Google TV Android app, Entertainment Space, Playstore Widget
val playbackUriAndroid =
PlatformSpecificUri.Builder()
.setPlatformType(PlatformType.TYPE_ANDROID_MOBILE)
.setActionUri(Uri.parse("https://www.example.com/entity_uri_for_android"))
.build()
// Optional. Set this when you want continue watching entities to show up on
// Google TV iOS app
val playbackUriIos =
PlatformSpecificUri.Builder()
.setPlatformType(PlatformType.TYPE_IOS)
.setActionUri(Uri.parse("https://www.example.com/entity_uri_for_ios"))
.build()
val platformSpecificPlaybackUris =
Arrays.asList(playbackUriTv, playbackUriAndroid, playbackUriIos)
Для изображений постеров требуется URI и размеры в пикселях (высота и ширина). Ориентируйтесь на различные форм-факторы, предоставляя несколько изображений постеров, но убедитесь, что все изображения поддерживают соотношение сторон 16:9 и минимальную высоту 200 пикселей для корректного отображения сущности «Продолжить просмотр», особенно в рамках Google Entertainment Space . Изображения высотой менее 200 пикселей могут не отображаться.
Image image1 = new Image.Builder()
.setImageUri(Uri.parse("http://www.example.com/entity_image1.png");)
.setImageHeightInPixel(300)
.setImageWidthInPixel(169)
.build()
Image image2 = new Image.Builder()
.setImageUri(Uri.parse("http://www.example.com/entity_image2.png");)
.setImageHeightInPixel(640)
.setImageWidthInPixel(360)
.build()
// And other images for different form factors.
val images = Arrays.asList(image1, image2)
MovieEntity
В этом примере показано, как создать MovieEntity
со всеми необходимыми полями:
val movieEntity = MovieEntity.Builder()
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Movie name")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
// Suppose the duration is 2 hours, it is 72000000 in milliseconds
.setDurationMills(72000000)
// Suppose last playback offset is 1 hour, 36000000 in milliseconds
.setLastPlayBackPositionTimeMillis(36000000)
.build()
Предоставление таких сведений, как жанры и рейтинги контента, дает Google TV возможность демонстрировать ваш контент более динамично и привлекать нужную аудиторию.
val genres = Arrays.asList("Action", "Science fiction");
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build();
val contentRatings = Arrays.asList(rating1);
val movieEntity = MovieEntity.Builder()
...
.addGenres(genres)
.addContentRatings(contentRatings)
.build()
Сущности автоматически остаются доступными в течение 60 дней, если вы не укажете более короткий срок действия. Устанавливайте пользовательское время действия только в том случае, если вам нужно, чтобы сущность была удалена до этого периода по умолчанию.
// Set the expiration time to be now plus 30 days in milliseconds
val expirationTime = new DisplayTimeWindow.Builder()
.setEndTimestampMillis(now().toMillis()+2592000000).build()
val movieEntity = MovieEntity.Builder()
...
.addAvailabilityTimeWindow(expirationTime)
.build()
TvEpisodeEntity
В этом примере показано, как создать TvEpisodeEntity
со всеми необходимыми полями:
val tvEpisodeEntity = TvEpisodeEntity.Builder()
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Episode name")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMills(72000000) // 2 hours in milliseconds
// 45 minutes and 15 seconds in milliseconds is 2715000
.setLastPlayBackPositionTimeMillis(2715000)
.setEpisodeNumber("2")
.setSeasonNumber("1")
.setShowTitle("Title of the show")
.build();
Строка номера эпизода (например, "2"
) и строка номера сезона (например, "1"
) будут расширены до надлежащей формы перед отображением на карточке продолжения просмотра. Обратите внимание, что они должны быть числовой строкой, не пишите "e2", "episode 2", "s1" или "season 1".
Если у конкретного телешоу один сезон, установите номер сезона как 1.
Чтобы максимизировать шансы зрителей найти ваш контент на Google TV, рассмотрите возможность предоставления дополнительных данных, таких как жанры, рейтинги контента и временные интервалы доступности, поскольку эти сведения могут улучшить параметры показа и фильтрации.
val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val tvEpisodeEntity = TvEpisodeEntity.Builder()
...
.addGenres(genres)
.addContentRatings(contentRatings)
.setSeasonTitle("Season Title")
.setShowTitle("Show Title)
.build();
ВидеоКлипЭнтити
Вот пример создания VideoClipEntity
со всеми необходимыми полями.
VideoClipEntity
представляет собой созданный пользователем клип, например видео на Youtube.
val videoClipEntity = VideoClipEntity.Builder()
.setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform")
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Video clip name")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMills(600000) //10 minutes in milliseconds
.setLastPlayBackPositionTimeMillis(300000) //5 minutes in milliseconds
.addContentRating(contentRating)
.build();
При желании вы можете указать создателя, изображение создателя, время создания в миллисекундах или временное окно доступности.
LiveStreamingVideoEntity
Вот пример создания LiveStreamingVideoEntity
со всеми обязательными полями.
val liveStreamingVideoEntity = LiveStreamingVideoEntity.Builder()
.setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform")
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Live streaming name")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMills(72000000) //2 hours in milliseconds
.setLastPlayBackPositionTimeMillis(36000000) //1 hour in milliseconds
.addContentRating(contentRating)
.build();
При желании вы можете задать время начала, вещателя, значок вещателя или временное окно доступности для объекта прямой трансляции.
Подробную информацию об атрибутах и требованиях см. в справочнике API .
Предоставить данные кластера продолжения
AppEngagePublishClient
отвечает за публикацию кластера Continuation. Вы используете метод publishContinuationCluster()
для публикации объекта ContinuationCluster
.
Сначала следует использовать isServiceAvailable(), чтобы проверить, доступна ли служба для интеграции.
client.publishContinuationCluster(
PublishContinuationClusterRequest
.Builder()
.setContinuationCluster(
ContinuationCluster
.Builder()
.setAccountProfile(accountProfile)
.addEntity(movieEntity1)
.addEntity(movieEntity2)
.addEntity(tvEpisodeEntity1)
.addEntity(tvEpisodeEntity2)
.setSyncAcrossDevices(true)
.build()
)
.build();
)
При получении запроса сервисом в рамках одной транзакции выполняются следующие действия:
- Существующие данные
ContinuationCluster
от партнера-разработчика удаляются. - Данные из запроса анализируются и сохраняются в обновленном
ContinuationCluster
.
В случае ошибки весь запрос отклоняется и сохраняется текущее состояние.
API публикации — это API обновления; он заменяет существующий контент. Если вам нужно обновить определенную сущность в ContinuationCluster, вам нужно будет снова опубликовать все сущности.
ПродолжениеДанные кластера должны предоставляться только для аккаунтов взрослых. Публикуйте только тогда, когда AccountProfile принадлежит взрослому.
Синхронизация между устройствами
Флаг SyncAcrossDevices
Этот флаг контролирует, синхронизируются ли данные ContinuationCluster пользователя на всех его устройствах (телевизор, телефон, планшет и т. д.). По умолчанию он имеет значение false, что означает, что синхронизация между устройствами по умолчанию отключена.
Ценности:
- true: ContinuationCluster данные распределяются между всеми устройствами пользователя для бесшовного просмотра. Мы настоятельно рекомендуем этот вариант для лучшего кросс-устройственного опыта.
- false: данные ContinuationCluster ограничены текущим устройством.
Получить согласие:
Медиа-приложение должно предоставлять четкую настройку для включения/выключения синхронизации между устройствами. Объясните преимущества пользователю и сохраните предпочтения пользователя один раз и примените их в publishContinuationCluster соответствующим образом.
// Example to allow cross device syncing.
client.publishContinuationCluster(
PublishContinuationClusterRequest
.Builder()
.setContinuationCluster(
ContinuationCluster
.Builder()
.setAccountProfile(accountProfile)
.setSyncAcrossDevices(true)
.build();
)
.build();
)
Чтобы максимально эффективно использовать нашу функцию кросс-устройства, убедитесь, что ваше приложение получает согласие пользователя, и включите SyncAcrossDevices в значение true. Это позволяет контенту беспрепятственно синхронизироваться между устройствами, что приводит к улучшению пользовательского опыта и повышению вовлеченности. Например, партнер, который реализовал это, увидел 40%-ное увеличение кликов «продолжить просмотр», поскольку его контент отображался на нескольких устройствах.
Удалить данные обнаружения видео
Чтобы вручную удалить данные пользователя с сервера Google TV до истечения стандартного 60-дневного срока хранения, используйте метод client.deleteClusters(). Получив запрос, сервис удалит все существующие данные обнаружения видео для профиля учетной записи или для всей учетной записи.
Перечисление DeleteReason
определяет причину удаления данных. Следующий код удаляет данные продолжения наблюдения при выходе из системы.
// If the user logs out from your media app, you must make the following call
// to remove continue watching data from the current google TV device,
// otherwise, the continue watching data will persist on the current
// google TV device until 60 days later.
client.deleteClusters(
new DeleteClustersRequest.Builder()
.setAccountProfile(AccountProfile())
.setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
.setSyncAcrossDevices(true)
.build()
)
Тестирование
Используйте приложение проверки, чтобы убедиться, что интеграция Engage SDK работает правильно. Это приложение Android предоставляет инструменты, которые помогут вам проверить данные и подтвердить, что намерения трансляции обрабатываются правильно.
После вызова API публикации подтвердите, что ваши данные публикуются правильно, проверив приложение проверки. Ваш кластер продолжения должен отображаться как отдельная строка в интерфейсе приложения.
- Убедитесь, что в файле манифеста Android вашего приложения флаг службы Engage НЕ установлен на производство.
- Установите и откройте приложение Engage Verify
- Если
isServiceAvailable
имеетfalse
, нажмите кнопку «Переключить», чтобы включить. - Введите имя пакета вашего приложения, чтобы автоматически просматривать опубликованные данные, как только вы начнете публикацию.
- Протестируйте эти действия в своем приложении:
- Войти.
- Переключение между профилями (если применимо).
- Запустите, затем поставьте видео на паузу или вернитесь на домашнюю страницу.
- Закройте приложение во время воспроизведения видео.
- Удалить элемент из строки «Продолжить просмотр» (если поддерживается).
- После каждого действия убедитесь, что ваше приложение вызвало API publishContinuationClusters и что данные правильно отображаются в приложении проверки.
Приложение для проверки покажет зеленую галочку «Все хорошо» для правильно реализованных объектов.
Рисунок 1. Успешное прохождение проверки приложения Приложение для проверки отметит все проблемные объекты.
Рисунок 2. Ошибка приложения проверки Для устранения неполадок с сущностями с ошибками используйте пульт ДУ телевизора, чтобы выбрать и нажать на сущность в приложении проверки. Конкретные проблемы будут отображены и выделены красным цветом для вашего обзора (см. пример ниже).
Рисунок 3. Подробности ошибки приложения проверки