В этом руководстве содержатся инструкции для разработчиков по интеграции рекомендуемого видеоконтента с использованием Engage SDK для формирования рекомендаций на различных платформах Google, таких как телевизор, мобильный телефон и планшет.
Система рекомендаций использует кластер рекомендаций для показа фильмов и телешоу из нескольких приложений в одной группе пользовательского интерфейса. Каждый партнер-разработчик может транслировать максимум 25 объектов в каждом кластере рекомендаций, и в одном запросе может быть максимум 7 кластеров рекомендаций.
Предварительная работа
Прежде чем начать, выполните следующие шаги.
Убедитесь, что ваше приложение использует API уровня 19 или выше для этой интеграции.
Добавьте библиотеку
com.google.android.engageв свое приложение.Для интеграции используются отдельные SDK: один для мобильных приложений и один для приложений для телевизоров.
Для мобильных устройств
dependencies { implementation 'com.google.android.engage:engage-core:1.5.9' }Для телевидения
dependencies { implementation 'com.google.android.engage:engage-tv:1.0.5' }В файле
AndroidManifest.xmlустановите для параметра Engage среду среды в режим production.Для мобильных устройств
<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>Выполнить публикацию в фоновом режиме.
Публиковать данные с рекомендациями не чаще одного раза в день, по одному из следующих событий:
- Первый вход пользователя в систему за день. ( или )
- Когда пользователь начинает взаимодействовать с приложением.
Интеграция
AppEngagePublishClient публикует кластер рекомендаций. Для публикации объекта рекомендаций используйте метод publishRecommendationClusters .
Используйте isServiceAvailable() , чтобы проверить, доступен ли сервис для интеграции.
val client = AppEngagePublishClient(context)
client.isServiceAvailable().addOnCompleteListener { task ->
if (task.isSuccessful) {
// Handle IPC call success
if(task.result) {
// Service is available on the device, proceed with content publish
// calls.
client.publishRecommendationClusters(recommendationRequest)
} else {
// Service is not available
}
} else {
// The IPC call itself fails, proceed with error handling logic here,
// such as retry.
}
}
Кластеры рекомендаций и запрос на публикацию
Кластеры представляют собой логические группы объектов. Приведенные ниже примеры кода показывают, как создавать кластеры в соответствии с вашими предпочтениями и как создавать запрос на публикацию для всех кластеров.
Параметр RecommendationClusterType определяет способ отображения кластера.
// cluster for popular movies
val recommendationCluster1 = RecommendationCluster
.Builder()
.addEntity(movie1)
.addEntity(movie2)
.addEntity(movie3)
.addEntity(movie4)
.addEntity(tvShow)
// This cluster is meant to be used as an individual provider row
.setRecommendationClusterType(TYPE_PROVIDER_ROW)
.setTitle("Popular Movies")
.build()
// cluster for live TV programs
val recommendationCluster2 = RecommendationCluster
.Builder()
.addEntity(liveTvProgramEntity1)
.addEntity(liveTvProgramEntity2)
.addEntity(liveTvProgramEntity3)
.addEntity(liveTvProgramEntity4)
.addEntity(liveTvProgramEntity5)
// This cluster is meant to be used as an individual provider row
.setRecommendationClusterType(TYPE_PROVIDER_ROW)
.setTitle("Popular Live TV Programs")
.build()
// creating a publishing request
val recommendationRequest = PublishRecommendationClustersRequest
.Builder()
.setSyncAcrossDevices(true)
.setAccountProfile(accountProfile)
.addRecommendationCluster(recommendationCluster1)
.addRecommendationCluster(recommendationCluster2)
.build()
Создать профиль учетной записи
Для персонализации работы на Google TV предоставьте информацию об учетной записи и профиле. Используйте раздел AccountProfile для предоставления следующих данных:
- Идентификатор учетной записи: уникальный идентификатор, представляющий учетную запись пользователя в вашем приложении. Это может быть фактический идентификатор учетной записи или его соответствующим образом зашифрованная версия.
- Идентификатор профиля (необязательно): Если ваше приложение поддерживает несколько профилей в рамках одной учетной записи, укажите уникальный идентификатор для конкретного профиля пользователя.
- Язык (необязательно): При желании вы можете указать предпочтительный язык пользователя. Это поле полезно, если вы отправляете
MediaActionFeedEntityв запросе на рекомендации.
// If app only supports account
val accountProfile = AccountProfile.Builder()
.setAccountId("account_id")
.build();
// If app supports both account and profile
val accountProfile = AccountProfile.Builder()
.setAccountId("account_id")
.setProfileId("profile_id")
.build();
// set Locale
val accountProfile = AccountProfile.Builder()
.setAccountId("account_id")
.setProfileId("profile_id")
.setLocale("en-US")
.build();
Когда сервис получает запрос, в рамках одной транзакции выполняются следующие действия:
- Существующие
RecommendationsClusterданные кластера от партнера-разработчика удаляются. - Данные из запроса анализируются и сохраняются в обновленном
RecommendationsCluster. В случае ошибки весь запрос отклоняется, и сохраняется текущее состояние.
Синхронизация между устройствами
Флаг SyncAcrossDevices определяет, будут ли данные кластера рекомендаций пользователя передаваться в Google TV и доступны ли они на всех устройствах пользователя, таких как телевизор, телефон, планшет. Для корректной работы рекомендаций этот флаг должен быть установлен в значение true.
Получить согласие
Приложение для работы с медиаконтентом должно предоставлять понятную настройку для включения или отключения синхронизации между устройствами. Объясните пользователю преимущества и сохраните его предпочтения один раз, а затем примените их в запросе publishRecommendations . Чтобы максимально эффективно использовать функцию синхронизации между устройствами, убедитесь, что приложение получило согласие пользователя и установило SyncAcrossDevices в true .
Удалите данные обнаружения видео.
Для ручного удаления данных пользователя с сервера Google TV до истечения стандартного 60-дневного срока хранения используйте метод client.deleteClusters() . После получения запроса сервис удалит все существующие данные о поиске видео для профиля учетной записи или для всей учетной записи.
Перечисление DeleteReason определяет причину удаления данных. Следующий код удаляет рекомендации при выходе из системы.
// If the user logs out from your media app, you must make the following call
// to remove recommendations data from the current google TV device,
// otherwise, the recommendations data persists on the current
// google TV device until 60 days later.
client.deleteClusters(
new DeleteClustersRequest.Builder()
.setAccountProfile(AccountProfile())
.setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
.build()
)
// If the user revokes the consent to share data with Google TV,
// you must make the following call to remove recommendations data from
// all current google TV devices. Otherwise, the recommendations data persists
// until 60 days later.
client.deleteClusters(
new DeleteClustersRequest.Builder()
.setAccountProfile(AccountProfile())
.setReason(DeleteReason.DELETE_REASON_LOSS_OF_CONSENT)
.build()
)
Создание сущностей
В SDK определены различные сущности для представления каждого типа элементов. Для кластера рекомендаций поддерживаются следующие сущности:
Описание
Для каждого объекта укажите краткое описание; это описание будет отображаться при наведении курсора на объект, предоставляя пользователям дополнительную информацию.
URI воспроизведения, специфичные для каждой платформы
Создайте URI воспроизведения для каждой поддерживаемой платформы: Android TV, Android или iOS. Это позволит системе выбрать соответствующий URI для воспроизведения видео на соответствующей платформе.
В тех редких случаях, когда URI воспроизведения совпадают для всех платформ, повторите процедуру для каждой платформы.
// Required. Set this when you want recommended 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()
// Optional. Set this when you want recommended entities to show up on
// Google TV Android app
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 recommended 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)
// Provide appropriate rating for the system.
val contentRating = new RatingSystem
.Builder()
.setAgencyName("MPAA")
.setRating("PG-13")
.build()
Изображения для плакатов
Для отображения изображений-постеров требуется URI и размеры в пикселях (высота и ширина). Для разных форматов используйте несколько изображений-постеров, но убедитесь, что все изображения имеют соотношение сторон 16:9 и минимальную высоту 200 пикселей для корректного отображения раздела «Рекомендации», особенно в развлекательном пространстве Google. Изображения высотой менее 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)
Причина рекомендации
При желании можно указать причину рекомендации, которую Google TV сможет использовать для формирования обоснования предложения пользователю конкретного фильма или телешоу.
//Allows us to construct reason: "Because it is top 10 on your Channel"
val topOnPartner = RecommendationReasonTopOnPartner
.Builder()
.setNum(10) //any valid integer value
.build()
//Allows us to construct reason: "Because it is popular on your Channel"
val popularOnPartner = RecommendationReasonPopularOnPartner
.Builder()
.build()
//Allows us to construct reason: "New to your channel, or Just added"
val newOnPartner = RecommendationReasonNewOnPartner
.Builder()
.build()
//Allows us to construct reason: "Because you watched Star Wars"
val watchedSimilarTitles = RecommendationReasonWatchedSimilarTitles
.addSimilarWatchedTitleName("Movie or TV Show Title")
.addSimilarWatchedTitleName("Movie or TV Show Title")
.Builder()
.build()
//Allows us to construct reason: "Recommended for you by ChannelName"
val recommendedForUser = RecommendationReasonRecommendedForUser
.Builder()
.build()
val watchAgain = RecommendationReasonWatchAgain
.Builder()
.build()
val fromUserWatchList = RecommendationReasonFromUserWatchlist
.Builder()
.build()
val userLikedOnPartner = RecommendationReasonUserLikedOnPartner
.Builder()
.setTitleName("Movie or TV Show Title")
.build()
val generic = RecommendationReasonGeneric.Builder().build()
Отображение временного окна
Если доступность объекта должна быть ограничена определенным временем, установите пользовательское время истечения срока действия. Без явного указания времени истечения срока действия объекты автоматически удаляются через 60 дней. Поэтому устанавливайте время истечения срока действия только в том случае, если необходимо, чтобы доступность объектов истекла раньше. Укажите несколько таких периодов доступности.
val window1 = DisplayTimeWindow
.Builder()
.setStartTimeStampMillis(now()+ 1.days.toMillis())
.setEndTimeStampMillis(now()+ 30.days.toMillis())
val window2 = DisplayTimeWindow
.Builder()
.setEndTimeStampMillis(now()+ 30.days.toMillis())
val availabilityTimeWindows: List<DisplayTimeWindow> = listof(window1,window2)
DataFeedElementId
Если вы интегрировали свой каталог медиафайлов или ленту действий с медиаконтентом с Google TV, вам не нужно создавать отдельные сущности для фильмов или телешоу. Вместо этого вы можете создать сущность MediaActionFeedEntity , которая включает обязательное поле DataFeedElementId. Этот идентификатор должен быть уникальным и совпадать с идентификатором в ленте действий с медиаконтентом, поскольку он помогает идентифицировать загруженный контент и выполнять поиск медиаконтента.
val id = "dataFeedEleemntId"
MovieEntity
Вот пример создания объекта MovieEntity со всеми необходимыми полями:
val movieEntity = MovieEntity.Builder()
.setName("Movie name")
.setDescription("A sentence describing movie.")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Suppose the duration is 2 hours, it is 72000000 in milliseconds
.setDurationMills(72000000)
.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);
//Suppose release date is 11-02-2025
val releaseDate = 1739233800000L
val movieEntity = MovieEntity.Builder()
...
.addGenres(genres)
.setReleaseDateEpochMillis(releaseDate)
.addContentRatings(contentRatings)
.setRecommendationReason(topOnPartner or watchedSimilarTitles)
.addAllAvailabilityTimeWindows(availabilityTimeWindows)
.build()
TvShowEntity
Вот пример создания объекта TvShowEntity со всеми необходимыми полями:
val tvShowEntity = TvShowEntity.Builder()
.setName("Show title")
.setDescription("A sentence describing TV Show.")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
.build();
При желании можно предоставить дополнительные данные, такие как жанры, рейтинги контента, причина рекомендации, цена предложения, количество сезонов или временной интервал доступности, которые могут быть использованы Google TV для улучшения отображения или фильтрации.
val genres = Arrays.asList("Action", "Science fiction");
val rating1 = RatingSystem.Builder()
.setAgencyName("MPAA")
.setRating("pg-13")
.build();
val price = Price.Builder()
.setCurrentPrice("$14.99")
.setStrikethroughPrice("$16.99")
.build();
val contentRatings = Arrays.asList(rating1);
val seasonCount = 5;
val tvShowEntity = TvShowEntity.Builder()
...
.addGenres(genres)
.addContentRatings(contentRatings)
.setRecommendationReason(topOnPartner or watchedSimilarTitles)
.addAllAvailabilityTimeWindows(availabilityTimeWindows)
.setSeasonCount(seasonCount)
.setPrice(price)
.build()
MediaActionFeedEntity
Вот пример создания объекта MediaActionFeedEntity со всеми необходимыми полями:
val mediaActionFeedEntity = MediaActionFeedEntity.Builder()
.setDataFeedElementId(id)
.build()
При желании можно предоставить дополнительные данные, такие как описание, причина рекомендации и временной интервал отображения, которые могут быть использованы Google TV для улучшения отображения или фильтрации.
val mediaActionFeedEntity = MediaActionFeedEntity.Builder()
.setName("Movie name or TV Show name")
.setDescription("A sentence describing an entity")
.setRecommendationReason(topOnPartner or watchedSimilarTitles)
.addPosterImages(images)
.build()
LiveTvChannelEntity
Это представляет собой телеканал в прямом эфире. Вот пример создания LiveTvChannelEntity со всеми необходимыми полями:
val liveTvChannelEntity = LiveTvChannelEntity.Builder()
.setName("Channel Name")
// ID of the live TV channel
.setEntityId("https://www.example.com/channel/12345")
.setDescription("A sentence describing this live TV channel.")
// channel playback uri must contain at least PlatformType.TYPE_ANDROID_TV
.addPlatformSpecificPlaybackUri(channelPlaybackUris)
.addLogoImage(logoImage)
.build()
При желании можно предоставить дополнительные данные, такие как рейтинг контента или причина рекомендации.
val rating1 = RatingSystem.Builder()
.setAgencyName("MPAA")
.setRating("pg-13")
.build()
val contentRatings = Arrays.asList(rating1)
val liveTvChannelEntity = LiveTvChannelEntity.Builder()
...
.addContentRatings(contentRatings)
.setRecommendationReason(topOnPartner)
.build()
LiveTvProgramEntity
Это представляет собой карточку телепрограммы, транслируемой или запланированной к трансляции на телеканале в прямом эфире. Вот пример создания объекта LiveTvProgramEntity со всеми необходимыми полями:
val liveTvProgramEntity = LiveTvProgramEntity.Builder()
// First set the channel information
.setChannelName("Channel Name")
.setChanelId("https://www.example.com/channel/12345")
// channel playback uri must contain at least PlatformType.TYPE_ANDROID_TV
.addPlatformSpecificPlaybackUri(channelPlaybackUris)
.setChannelLogoImage(channelLogoImage)
// Then set the program or card specific information.
.setName("Program Name")
.setEntityId("https://www.example.com/schedule/123")
.setDescription("Program Desccription")
.addAvailabilityTimeWindow(
DisplayTimeWindow.Builder()
.setStartTimestampMillis(1756713600000L)// 2025-09-01T07:30:00+0000
.setEndTimestampMillis(1756715400000L))// 2025-09-01T08:00:00+0000
.addPosterImage(programImage)
.build()
При желании можно указать дополнительные данные, такие как рейтинг контента, жанры или обоснование рекомендации.
val rating1 = RatingSystem.Builder()
.setAgencyName("MPAA")
.setRating("pg-13")
.build()
val contentRatings = Arrays.asList(rating1)
val genres = Arrays.asList("Action", "Science fiction")
val liveTvProgramEntity = LiveTvProgramEntity.Builder()
...
.addContentRatings(contentRatings)
.addGenres(genres)
.setRecommendationReason(topOnPartner)
.build()
Внедрение этих шагов позволит разработчикам успешно интегрировать рекомендации видеоконтента в Google TV, повышая узнаваемость и вовлеченность пользователей, а также обеспечивая единообразный и персонализированный просмотр на всех устройствах.