Google создает поверхность на устройстве, которая организует приложения пользователей по вертикалям и обеспечивает новый захватывающий опыт для персонализированного потребления и обнаружения контента приложений. Этот полноэкранный режим дает партнерам-разработчикам возможность продемонстрировать свой лучший насыщенный контент на специальном канале за пределами своего приложения.
В этом руководстве содержатся инструкции для партнеров-разработчиков по интеграции своего видеоконтента с помощью Engage SDK для заполнения как этой новой области, так и существующих поверхностей Google.
Детали интеграции
Терминология
Эта интеграция включает следующие три типа кластеров: Рекомендация , Продолжение и Рекомендуемые .
Кластеры рекомендаций отображают персонализированные предложения по контенту для просмотра от отдельного партнера-разработчика.
Ваши рекомендации имеют следующую структуру:
Кластер рекомендаций: представление пользовательского интерфейса, содержащее группу рекомендаций от одного и того же партнера-разработчика.
Сущность: объект, представляющий один элемент в кластере. Объектом может быть фильм, телешоу, телесериал, живое видео и многое другое. Список поддерживаемых типов объектов см. в разделе «Предоставление данных объекта» .
Кластер «Продолжение» показывает незаконченные видеоролики и соответствующие недавно выпущенные эпизоды от нескольких партнеров-разработчиков в одной группе пользовательского интерфейса. Каждому партнеру-разработчику будет разрешено транслировать максимум 10 сущностей в кластере продолжения. Исследования показали, что персонализированные рекомендации вместе с персонализированным контентом-продолжением обеспечивают максимальную вовлеченность пользователей.
В кластере «Рекомендуемые» представлены объекты от нескольких партнеров-разработчиков в одной группе пользовательского интерфейса. Будет один кластер «Рекомендуемые», который будет отображаться в верхней части пользовательского интерфейса с приоритетом над всеми кластерами «Рекомендации». Каждому партнеру-разработчику будет разрешено транслировать до 10 объектов в избранном кластере.
Предварительная работа
Минимальный уровень API: 19
Добавьте библиотеку com.google.android.engage:engage-core
в свое приложение:
dependencies {
// Make sure you also include that repository in your project's build.gradle file.
implementation 'com.google.android.engage:engage-core:1.5.2'
}
Дополнительную информацию см. в разделе Видимость пакетов в Android 11 .
Краткое содержание
Проект основан на реализации привязанного сервиса .
На данные, которые клиент может публиковать, распространяются следующие ограничения для разных типов кластеров:
Тип кластера | Ограничения кластера | Максимальные ограничения объектов в кластере |
---|---|---|
Кластер(ы) рекомендаций | Максимум 5 | Максимум 50 |
Продолжение кластера | Максимум 1 | Максимум 10 |
Рекомендуемый кластер | Максимум 1 | Максимум 10 |
Шаг 0: Миграция с существующей интеграции Media Home SDK
Сопоставление моделей данных из существующей интеграции
Если вы переходите от существующей интеграции Media Home, в следующей таблице показано, как сопоставить модели данных в существующих SDK с новым Engage SDK:
Эквивалент интеграции MediaHomeVideoContract | Эквивалент интеграции Engage SDK |
---|---|
com.google.android.mediahome.video.PreviewChannel | com.google.android.engage.common.datamodel.RecommendationCluster |
com.google.android.mediahome.video.PreviewChannel.Builder | com.google.android.engage.common.datamodel.RecommendationCluster.Builder |
com.google.android.mediahome.video.PreviewChannelHelper | com.google.android.engage.video.service.AppEngageVideoClient |
com.google.android.mediahome.video.PreviewProgram | Разделены на отдельные классы: EventVideo , LiveStreamingVideo , Movie , TvEpisode , TvSeason , TvShow , VideoClipEntity |
com.google.android.mediahome.video.PreviewProgram.Builder | Разделены построители на отдельные классы: EventVideo , LiveStreamingVideo , Movie , TvEpisode , TvSeason , TvShow , VideoClipEntity |
com.google.android.mediahome.video.VideoContract | Больше не нужен. |
com.google.android.mediahome.video.WatchNextProgram | Разделены на атрибуты в отдельные классы: EventVideoEntity , LiveStreamingVideoEntity , MovieEntity , TvEpisodeEntity , TvSeasonEntity , TvShowEntity , VideoClipEntity |
com.google.android.mediahome.video.WatchNextProgram.Builder | Разделены на атрибуты в отдельные классы: EventVideoEntity , LiveStreamingVideoEntity , MovieEntity , TvEpisodeEntity , TvSeasonEntity , TvShowEntity , VideoClipEntity |
Публикация кластеров в Media Home SDK и Engage SDK
С помощью Media Home SDK кластеры и объекты публиковались через отдельные API:
// 1. Fetch existing channels
List<PreviewChannel> channels = PreviewChannelHelper.getAllChannels();
// 2. If there are no channels, publish new channels
long channelId = PreviewChannelHelper.publishChannel(builder.build());
// 3. If there are existing channels, decide whether to update channel contents
PreviewChannelHelper.updatePreviewChannel(channelId, builder.build());
// 4. Delete all programs in the channel
PreviewChannelHelper.deleteAllPreviewProgramsByChannelId(channelId);
// 5. publish new programs in the channel
PreviewChannelHelper.publishPreviewProgram(builder.build());
С помощью Engage SDK публикация кластера и объекта объединяется в один вызов API. Все сущности, принадлежащие кластеру, публикуются вместе с этим кластером:
Котлин
RecommendationCluster.Builder() .addEntity(MOVIE_ENTITY) .addEntity(MOVIE_ENTITY) .addEntity(MOVIE_ENTITY) .setTitle("Top Picks For You") .build()
Ява
new RecommendationCluster.Builder() .addEntity(MOVIE_ENTITY) .addEntity(MOVIE_ENTITY) .addEntity(MOVIE_ENTITY) .setTitle("Top Picks For You") .build();
Шаг 1. Предоставьте данные об объекте
В SDK определены разные объекты для представления каждого типа элементов. Мы поддерживаем следующие объекты в категории «Наблюдение»:
В следующей таблице представлены атрибуты и требования для каждого типа.
MovieEntity
Атрибут | Требование | Примечания |
---|---|---|
Имя | Необходимый | |
Изображения для плакатов | Необходимый | Требуется хотя бы одно изображение с указанием соотношения сторон. (Предпочтителен пейзаж, но для разных сценариев рекомендуется использовать как портретные, так и альбомные изображения.) Дополнительные сведения см. в разделе «Спецификации изображения» . |
Воспроизведение URI | Необходимый | Глубокая ссылка на приложение поставщика, позволяющее начать воспроизведение фильма. Примечание. Для атрибуции можно использовать глубокие ссылки. Обратитесь к этому FAQ |
URI информационной страницы | Необязательный | Глубокая ссылка на приложение поставщика, где можно просмотреть подробную информацию о фильме. Примечание. Для атрибуции можно использовать глубокие ссылки. Обратитесь к этому FAQ |
Дата выпуска | Необходимый | В эпоху миллисекунд. |
Доступность | Необходимый | ДОСТУПНО: контент доступен пользователю без каких-либо дополнительных действий. FREE_WITH_SUBSCRIPTION: контент доступен после приобретения пользователем подписки. PAID_CONTENT: контент требует покупки или аренды пользователем. КУПЛЕНО: контент был куплен или арендован пользователем. |
Цена предложения | Необязательный | Произвольный текст |
Продолжительность | Необходимый | В миллисекундах. |
Жанр | Необходимый | Произвольный текст |
Рейтинги контента | Необходимый | Свободный текст, следуйте отраслевым стандартам. ( Пример ) |
Смотреть следующий тип | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, и должен быть одним из следующих четырех типов: ПРОДОЛЖИТЬ: пользователь уже просмотрел этот контент более 1 минуты. НОВИНКА: Пользователь посмотрел все доступные серии из какого-то эпизодического контента, но стала доступна новая серия и осталась ровно одна непросмотренная серия. Это работает для телешоу, записанных футбольных матчей в серии и так далее. ДАЛЕЕ: Пользователь посмотрел одну или несколько полных серий из некоторого эпизодического контента, но осталось либо более одной серии, либо остался ровно один эпизод, причем последний эпизод не является «НОВЫМ» и был выпущен до того, как пользователь начал просмотр эпизодического контента. . СПИСОК ПРОСМОТРА: Пользователь явно решил добавить фильм, событие или сериал в список просмотра, чтобы вручную выбирать, что он хочет посмотреть дальше. |
Время последнего взаимодействия | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения. В эпоху миллисекунд. |
Время последней позиции воспроизведения | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, а WatchNextType имеет значение CONTINUE. В эпоху миллисекунд. |
TvShowEntity
Атрибут | Требование | Примечания |
---|---|---|
Имя | Необходимый | |
Изображения для плакатов | Необходимый | Требуется хотя бы одно изображение с указанием соотношения сторон. (Предпочтителен пейзаж, но для разных сценариев рекомендуется использовать как портретные, так и альбомные изображения.) Дополнительные сведения см. в разделе «Спецификации изображения» . |
URI информационной страницы | Необходимый | Глубокая ссылка на приложение провайдера, где можно просмотреть подробную информацию о телешоу. Примечание. Для атрибуции можно использовать глубокие ссылки. Обратитесь к этому FAQ |
Воспроизведение URI | Необязательный | Глубокая ссылка на приложение провайдера, позволяющее начать воспроизведение телешоу. Примечание. Для атрибуции можно использовать глубокие ссылки. Обратитесь к этому FAQ |
Дата выхода первой серии | Необходимый | В эпоху миллисекунд. |
Дата выхода последней серии | Необязательный | В эпоху миллисекунд. |
Доступность | Необходимый | ДОСТУПНО: контент доступен пользователю без каких-либо дополнительных действий. FREE_WITH_SUBSCRIPTION: контент доступен после приобретения пользователем подписки. PAID_CONTENT: контент требует покупки или аренды пользователем. КУПЛЕНО: контент был куплен или арендован пользователем. |
Цена предложения | Необязательный | Произвольный текст |
Количество сезонов | Необходимый | Положительное целое число |
Жанр | Необходимый | Произвольный текст |
Рейтинги контента | Необходимый | Свободный текст, следуйте отраслевым стандартам. ( Пример ) |
Смотреть следующий тип | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, и должен быть одним из следующих четырех типов: ПРОДОЛЖИТЬ: пользователь уже просмотрел этот контент более 1 минуты. НОВИНКА: Пользователь посмотрел все доступные серии из какого-то эпизодического контента, но стала доступна новая серия и осталась ровно одна непросмотренная серия. Это работает для телешоу, записанных футбольных матчей в серии и так далее. ДАЛЕЕ: Пользователь посмотрел одну или несколько полных серий из некоторого эпизодического контента, но осталось либо более одной серии, либо остался ровно один эпизод, причем последний эпизод не является «НОВЫМ» и был выпущен до того, как пользователь начал просмотр эпизодического контента. . СПИСОК ПРОСМОТРА: Пользователь явно решил добавить фильм, событие или сериал в список просмотра, чтобы вручную выбирать, что он хочет посмотреть дальше. |
Время последнего взаимодействия | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения. В эпоху миллисекунд. |
Время последней позиции воспроизведения | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, а WatchNextType имеет значение CONTINUE. В эпоху миллисекунд. |
TvSeasonEntity
Атрибут | Требование | Примечания |
---|---|---|
Имя | Необходимый | |
Изображения для плакатов | Необходимый | Требуется хотя бы одно изображение с указанием соотношения сторон. (Предпочтителен пейзаж, но для разных сценариев рекомендуется использовать как портретные, так и альбомные изображения.) Дополнительные сведения см. в разделе «Спецификации изображения» . |
URI информационной страницы | Необходимый | Глубокая ссылка на приложение провайдера, где можно просмотреть подробную информацию о сезоне телешоу. Примечание. Для атрибуции можно использовать глубокие ссылки. Обратитесь к этому FAQ |
Воспроизведение URI | Необязательный | Глубокая ссылка на приложение провайдера, позволяющее начать воспроизведение сезона телешоу. Примечание. Для атрибуции можно использовать глубокие ссылки. Обратитесь к этому FAQ |
Отображение номера сезона | Необязательный Доступно в версии 1.3.1 | Нить |
Дата выхода первой серии | Необходимый | В эпоху миллисекунд. |
Дата выхода последней серии | Необязательный | В эпоху миллисекунд. |
Доступность | Необходимый | ДОСТУПНО: контент доступен пользователю без каких-либо дополнительных действий. FREE_WITH_SUBSCRIPTION: контент доступен после приобретения пользователем подписки. PAID_CONTENT: контент требует покупки или аренды пользователем. КУПЛЕНО: контент был куплен или арендован пользователем. |
Цена предложения | Необязательный | Произвольный текст |
Количество эпизодов | Необходимый | Положительное целое число |
Жанр | Необходимый | Произвольный текст |
Рейтинги контента | Необходимый | Свободный текст, следуйте отраслевым стандартам. ( Пример ) |
Смотреть следующий тип | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, и должен быть одним из следующих четырех типов: ПРОДОЛЖИТЬ: пользователь уже просмотрел этот контент более 1 минуты. НОВИНКА: Пользователь посмотрел все доступные серии из какого-то эпизодического контента, но стала доступна новая серия и осталась ровно одна непросмотренная серия. Это работает для телешоу, записанных футбольных матчей в серии и так далее. ДАЛЕЕ: Пользователь посмотрел одну или несколько полных серий из некоторого эпизодического контента, но осталось либо более одной серии, либо остался ровно один эпизод, причем последний эпизод не является «НОВЫМ» и был выпущен до того, как пользователь начал просмотр эпизодического контента. . СПИСОК ПРОСМОТРА: Пользователь явно решил добавить фильм, событие или сериал в список просмотра, чтобы вручную выбирать, что он хочет посмотреть дальше. |
Время последнего взаимодействия | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения. В эпоху миллисекунд. |
Время последней позиции воспроизведения | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, а WatchNextType имеет значение CONTINUE. В эпоху миллисекунд. |
TvEpisodeEntity
Атрибут | Требование | Примечания |
---|---|---|
Имя | Необходимый | |
Изображения для плакатов | Необходимый | Требуется хотя бы одно изображение с указанием соотношения сторон. (Предпочтителен пейзаж, но для разных сценариев рекомендуется использовать как портретные, так и альбомные изображения.) Дополнительные сведения см. в разделе «Спецификации изображения» . |
Воспроизведение URI | Необходимый | Глубокая ссылка на приложение поставщика, позволяющее начать воспроизведение эпизода. Примечание. Для атрибуции можно использовать глубокие ссылки. Обратитесь к этому FAQ |
URI информационной страницы | Необязательный | Глубокая ссылка на приложение провайдера, где можно просмотреть подробную информацию об эпизоде телешоу. Примечание. Для атрибуции можно использовать глубокие ссылки. Обратитесь к этому FAQ |
Отобразить номер эпизода | Необязательный Доступно в версии 1.3.1 | Нить |
Дата выхода в эфир | Необходимый | В эпоху миллисекунд. |
Доступность | Необходимый | ДОСТУПНО: контент доступен пользователю без каких-либо дополнительных действий. FREE_WITH_SUBSCRIPTION: контент доступен после приобретения пользователем подписки. PAID_CONTENT: контент требует покупки или аренды пользователем. КУПЛЕНО: контент был куплен или арендован пользователем. |
Цена предложения | Необязательный | Произвольный текст |
Продолжительность | Необходимый | Должно быть положительным значением в миллисекундах. |
Жанр | Необходимый | Произвольный текст |
Рейтинги контента | Необходимый | Свободный текст, следуйте отраслевым стандартам. ( Пример ) |
Смотреть следующий тип | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, и должен быть одним из следующих четырех типов: ПРОДОЛЖИТЬ: пользователь уже просмотрел этот контент более 1 минуты. НОВИНКА: Пользователь посмотрел все доступные серии из какого-то эпизодического контента, но стала доступна новая серия и осталась ровно одна непросмотренная серия. Это работает для телешоу, записанных футбольных матчей в серии и так далее. ДАЛЕЕ: Пользователь посмотрел одну или несколько полных серий из некоторого эпизодического контента, но осталось либо более одной серии, либо остался ровно один эпизод, причем последний эпизод не является «НОВЫМ» и был выпущен до того, как пользователь начал просмотр эпизодического контента. . СПИСОК ПРОСМОТРА: Пользователь явно решил добавить фильм, событие или сериал в список просмотра, чтобы вручную выбирать, что он хочет посмотреть дальше. |
Время последнего взаимодействия | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения. В эпоху миллисекунд. |
Время последней позиции воспроизведения | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, а WatchNextType имеет значение CONTINUE. В эпоху миллисекунд. |
LiveStreamingVideoEntity
Атрибут | Требование | Примечания |
---|---|---|
Имя | Необходимый | |
Изображения для плакатов | Необходимый | Требуется хотя бы одно изображение с указанием соотношения сторон. (Предпочтителен пейзаж, но для разных сценариев рекомендуется использовать как портретные, так и альбомные изображения.) Дополнительные сведения см. в разделе «Спецификации изображения» . |
Воспроизведение URI | Необходимый | Глубокая ссылка на приложение поставщика, позволяющая начать воспроизведение видео. Примечание. Для атрибуции можно использовать глубокие ссылки. Обратитесь к этому FAQ |
Вещатель | Необходимый | Произвольный текст |
Время начала | Необязательный | В эпоху миллисекунд. |
Время окончания | Необязательный | В эпоху миллисекунд. |
Количество просмотров | Необязательный | Произвольный текст, должен быть локализован. |
Смотреть следующий тип | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, и должен быть одним из следующих четырех типов: ПРОДОЛЖИТЬ: Пользователь уже просмотрел этот контент более 1 минуты. НОВИНКА: Пользователь посмотрел все доступные серии из какого-то эпизодического контента, но стала доступна новая серия и осталась ровно одна непросмотренная серия. Это работает для телепередач, записанных футбольных матчей в серии и так далее. ДАЛЕЕ: Пользователь посмотрел одну или несколько полных серий из некоторого эпизодического контента, но осталось либо более одной серии, либо остался ровно один эпизод, причем последний эпизод не является «НОВЫМ» и был выпущен до того, как пользователь начал просмотр эпизодического контента. . СПИСОК ПРОСМОТРА: пользователь явно решил добавить фильм, событие или сериал в список просмотра, чтобы вручную выбрать, что он хочет посмотреть дальше. |
Время последнего взаимодействия | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения. В эпоху миллисекунд. |
Время последней позиции воспроизведения | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, а WatchNextType имеет значение CONTINUE. В эпоху миллисекунд. |
VideoClipEntity
Объект VideoClipEntity
представляет собой видеообъект, полученный из социальных сетей, таких как TikTok или YouTube.
Атрибут | Требование | Примечания |
---|---|---|
Имя | Необходимый | |
Изображения для плакатов | Необходимый | Требуется хотя бы одно изображение с указанием соотношения сторон. (Предпочтителен пейзаж, но для разных сценариев рекомендуется использовать как портретные, так и альбомные изображения.) Дополнительные сведения см. в разделе «Спецификации изображения» . |
Воспроизведение URI | Необходимый | Глубокая ссылка на приложение поставщика, позволяющая начать воспроизведение видео. Примечание. Для атрибуции можно использовать глубокие ссылки. Обратитесь к этому FAQ |
Время создания | Необходимый | В эпоху миллисекунд. |
Продолжительность | Необходимый | Должно быть положительным значением в миллисекундах. |
Создатель | Необходимый | Произвольный текст |
Изображение автора | Необязательный | Изображение аватара Создателя |
Количество просмотров | Необязательный | Произвольный текст, должен быть локализован. |
Смотреть следующий тип | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, и должен быть одним из следующих четырех типов: ПРОДОЛЖИТЬ: Пользователь уже просмотрел этот контент более 1 минуты. НОВИНКА: Пользователь посмотрел все доступные серии из какого-то эпизодического контента, но стала доступна новая серия и осталась ровно одна непросмотренная серия. Это работает для телепередач, записанных футбольных матчей в серии и так далее. ДАЛЕЕ: Пользователь посмотрел одну или несколько полных серий из некоторого эпизодического контента, но осталось либо более одной серии, либо остался ровно один эпизод, причем последний эпизод не является «НОВЫМ» и был выпущен до того, как пользователь начал просмотр эпизодического контента. . СПИСОК ПРОСМОТРА: Пользователь явно решил добавить фильм, событие или сериал в список просмотра, чтобы вручную выбирать, что он хочет посмотреть дальше. |
Время последнего взаимодействия | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения. В эпоху миллисекунд. |
Время последней позиции воспроизведения | Условно требуется | Должен быть предоставлен, когда элемент находится в кластере продолжения, а WatchNextType имеет значение CONTINUE. В эпоху миллисекунд. |
Характеристики изображения
В следующем разделе перечислены необходимые спецификации для графических ресурсов:
Форматы файлов
PNG, JPG, статический GIF, WebP
Максимальный размер файла
5120 КБ
Дополнительные рекомендации
- Безопасная область изображения: поместите важный контент в центр 80% изображения.
Пример
Котлин
var movie = MovieEntity.Builder() .setName("Avengers") .addPosterImage(Image.Builder() .setImageUri(Uri.parse("http://www.x.com/image.png")) .setImageHeightInPixel(960) .setImageWidthInPixel(408) .build()) .setPlayBackUri(Uri.parse("http://tv.com/playback/1")) .setReleaseDateEpochMillis(1633032895L) .setAvailability(ContentAvailability.AVAILABILITY_AVAILABLE) .setDurationMillis(12345678L) .addGenre("action") .addContentRating("R") .setWatchNextType(WatchNextType.TYPE_NEW) .setLastEngagementTimeMillis(1664568895L) .build()
Ява
MovieEntity movie = new MovieEntity.Builder() .setName("Avengers") .addPosterImage( new Image.Builder() .setImageUri(Uri.parse("http://www.x.com/image.png")) .setImageHeightInPixel(960) .setImageWidthInPixel(408) .build()) .setPlayBackUri(Uri.parse("http://tv.com/playback/1")) .setReleaseDateEpochMillis(1633032895L) .setAvailability(ContentAvailability.AVAILABILITY_AVAILABLE) .setDurationMillis(12345678L) .addGenre("action") .addContentRating("R") .setWatchNextType(WatchNextType.TYPE_NEW) .setLastEngagementTimeMillis(1664568895L) .build();
Шаг 2. Предоставьте данные кластера
Рекомендуется, чтобы задание публикации контента выполнялось в фоновом режиме (например, с помощью WorkManager ) и планировалось на регулярной основе или на основе событий (например, каждый раз, когда пользователь открывает приложение или когда пользователь только что добавил что-то в свой тележка).
AppEngagePublishClient
отвечает за публикацию кластеров. В клиенте доступны следующие API:
-
isServiceAvailable
-
publishRecommendationClusters
-
publishFeaturedCluster
-
publishContinuationCluster
-
publishUserAccountManagementRequest
-
updatePublishStatus
-
deleteRecommendationsClusters
-
deleteFeaturedCluster
-
deleteContinuationCluster
-
deleteUserManagementCluster
-
deleteClusters
isServiceAvailable
Этот API используется для проверки доступности сервиса для интеграции и возможности представления контента на устройстве.
Котлин
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. } else { // Service is not available, no further action is needed. } } else { // The IPC call itself fails, proceed with error handling logic here, // such as retry. } }
Ява
client.isServiceAvailable().addOnCompleteListener(task - > { if (task.isSuccessful()) { // Handle success if(task.getResult()) { // Service is available on the device, proceed with content publish // calls. } else { // Service is not available, no further action is needed. } } else { // The IPC call itself fails, proceed with error handling logic here, // such as retry. } });
publishRecommendationClusters
Этот API используется для публикации списка объектов RecommendationCluster
.
Котлин
client.publishRecommendationClusters( PublishRecommendationClustersRequest.Builder() .addRecommendationCluster( RecommendationCluster.Builder() .addEntity(entity1) .addEntity(entity2) .setTitle("Top Picks For You") .build() ) .build() )
Ява
client.publishRecommendationClusters( new PublishRecommendationClustersRequest.Builder() .addRecommendationCluster( new RecommendationCluster.Builder() .addEntity(entity1) .addEntity(entity2) .setTitle("Top Picks For You") .build()) .build());
Когда сервис получает запрос, в рамках одной транзакции происходят следующие действия:
- Существующие данные
RecommendationCluster
от партнера-разработчика удаляются. - Данные запроса анализируются и сохраняются в обновленном кластере рекомендаций.
В случае ошибки весь запрос отклоняется и существующее состояние сохраняется.
publishFeaturedCluster
Этот API используется для публикации списка объектов FeaturedCluster
.
Котлин
client.publishFeaturedCluster( PublishFeaturedClusterRequest.Builder() .setFeaturedCluster( FeaturedCluster.Builder() .addEntity(entity1) .addEntity(entity2) .build()) .build())
Ява
client.publishFeaturedCluster( new PublishFeaturedClustersRequest.Builder() .addFeaturedCluster( new FeaturedCluster.Builder() .addEntity(entity1) .addEntity(entity2) .build()) .build());
Когда сервис получает запрос, в рамках одной транзакции происходят следующие действия:
- Существующие данные
FeaturedCluster
от партнера-разработчика удаляются. - Данные запроса анализируются и сохраняются в обновленном избранном кластере.
В случае ошибки весь запрос отклоняется и существующее состояние сохраняется.
publishContinuationCluster
Этот API используется для публикации объекта ContinuationCluster
.
Котлин
client.publishContinuationCluster( PublishContinuationClusterRequest.Builder() .setContinuationCluster( ContinuationCluster.Builder() .addEntity(entity1) .addEntity(entity2) .build()) .build())
Ява
client.publishContinuationCluster( new PublishContinuationClusterRequest.Builder() .setContinuationCluster( new ContinuationCluster.Builder() .addEntity(entity1) .addEntity(entity2) .build()) .build());
Когда сервис получает запрос, в рамках одной транзакции происходят следующие действия:
- Существующие данные
ContinuationCluster
от партнера-разработчика удаляются. - Данные запроса анализируются и сохраняются в обновленном кластере продолжения.
В случае ошибки весь запрос отклоняется и существующее состояние сохраняется.
publishUserAccountManagementRequest
Этот API используется для публикации карты входа. Действие входа направляет пользователей на страницу входа в приложение, чтобы приложение могло публиковать контент (или предоставлять более персонализированный контент).
Следующие метаданные являются частью карты входа:
Атрибут | Требование | Описание |
---|---|---|
Действие Ури | Необходимый | Глубокая ссылка на действие (т. е. переход на страницу входа в приложение) |
Изображение | Необязательно: если не указано, необходимо указать название. | Изображение на карте Изображения с соотношением сторон 16x9 и разрешением 1264x712. |
Заголовок | Необязательно: если не указано, необходимо предоставить изображение. | Титул на карте |
Текст действия | Необязательный | Текст, отображаемый в призыве к действию (т. е. войдите в систему) |
Субтитры | Необязательный | Дополнительный субтитр на карте |
Котлин
var SIGN_IN_CARD_ENTITY = SignInCardEntity.Builder() .addPosterImage( Image.Builder() .setImageUri(Uri.parse("http://www.x.com/image.png")) .setImageHeightInPixel(500) .setImageWidthInPixel(500) .build()) .setActionText("Sign In") .setActionUri(Uri.parse("http://xx.com/signin")) .build() client.publishUserAccountManagementRequest( PublishUserAccountManagementRequest.Builder() .setSignInCardEntity(SIGN_IN_CARD_ENTITY) .build());
Ява
SignInCardEntity SIGN_IN_CARD_ENTITY = new SignInCardEntity.Builder() .addPosterImage( new Image.Builder() .setImageUri(Uri.parse("http://www.x.com/image.png")) .setImageHeightInPixel(500) .setImageWidthInPixel(500) .build()) .setActionText("Sign In") .setActionUri(Uri.parse("http://xx.com/signin")) .build(); client.publishUserAccountManagementRequest( new PublishUserAccountManagementRequest.Builder() .setSignInCardEntity(SIGN_IN_CARD_ENTITY) .build());
Когда сервис получает запрос, в рамках одной транзакции происходят следующие действия:
- Существующие данные
UserAccountManagementCluster
от партнера-разработчика удаляются. - Данные запроса анализируются и сохраняются в обновленном кластере UserAccountManagementCluster.
В случае ошибки весь запрос отклоняется и существующее состояние сохраняется.
updatePublishStatus
Если по какой-либо внутренней деловой причине ни один из кластеров не опубликован, мы настоятельно рекомендуем обновить статус публикации с помощью API updatePublishStatus . Это важно, потому что:
- Предоставление статуса во всех сценариях, даже когда контент опубликован (СТАТУС == ПУБЛИКИРОВАНО), имеет решающее значение для заполнения панелей мониторинга, которые используют этот явный статус для передачи работоспособности и других показателей вашей интеграции.
- Если контент не опубликован, но статус интеграции не нарушен (STATUS == NOT_PUBLISHED), Google может избежать появления оповещений на панелях состояния приложения. Это подтверждает, что контент не публикуется по причине ожидаемой с точки зрения провайдера ситуации.
- Это помогает разработчикам получить представление о том, когда данные публикуются, а когда нет.
- Google может использовать коды состояния, чтобы подтолкнуть пользователя к выполнению определенных действий в приложении, чтобы он мог увидеть содержимое приложения или преодолеть его.
Список подходящих кодов статуса публикации:
// Content is published
AppEngagePublishStatusCode.PUBLISHED,
// Content is not published as user is not signed in
AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SIGN_IN,
// Content is not published as user is not subscribed
AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SUBSCRIPTION,
// Content is not published as user location is ineligible
AppEngagePublishStatusCode.NOT_PUBLISHED_INELIGIBLE_LOCATION,
// Content is not published as there is no eligible content
AppEngagePublishStatusCode.NOT_PUBLISHED_NO_ELIGIBLE_CONTENT,
// Content is not published as the feature is disabled by the client
// Available in v1.3.1
AppEngagePublishStatusCode.NOT_PUBLISHED_FEATURE_DISABLED_BY_CLIENT,
// Content is not published as the feature due to a client error
// Available in v1.3.1
AppEngagePublishStatusCode.NOT_PUBLISHED_CLIENT_ERROR,
// Content is not published as the feature due to a service error
// Available in v1.3.1
AppEngagePublishStatusCode.NOT_PUBLISHED_SERVICE_ERROR,
// Content is not published due to some other reason
// Reach out to engage-developers@ before using this enum.
AppEngagePublishStatusCode.NOT_PUBLISHED_OTHER
Если контент не публикуется из-за того, что пользователь не вошел в систему, Google рекомендует опубликовать карту входа. Если по какой-либо причине поставщики не могут опубликовать карту входа, мы рекомендуем вызвать API updatePublishStatus с кодом состояния NOT_PUBLISHED_REQUIRES_SIGN_IN.
Котлин
client.updatePublishStatus( PublishStatusRequest.Builder() .setStatusCode(AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SIGN_IN) .build())
Ява
client.updatePublishStatus( new PublishStatusRequest.Builder() .setStatusCode(AppEngagePublishStatusCode.NOT_PUBLISHED_REQUIRES_SIGN_IN) .build());
deleteRecommendationClusters
Этот API используется для удаления содержимого кластеров рекомендаций.
Котлин
client.deleteRecommendationClusters()
Ява
client.deleteRecommendationClusters();
Когда служба получает запрос, она удаляет существующие данные из кластеров рекомендаций. В случае ошибки весь запрос отклоняется и существующее состояние сохраняется.
deleteFeaturedCluster
Этот API используется для удаления содержимого избранного кластера.
Котлин
client.deleteFeaturedCluster()
Ява
client.deleteFeaturedCluster();
Когда служба получает запрос, она удаляет существующие данные из избранного кластера. В случае ошибки весь запрос отклоняется и существующее состояние сохраняется.
deleteContinuationCluster
Этот API используется для удаления содержимого кластера продолжения.
Котлин
client.deleteContinuationCluster()
Ява
client.deleteContinuationCluster();
Когда служба получает запрос, она удаляет существующие данные из кластера продолжения. В случае ошибки весь запрос отклоняется и существующее состояние сохраняется.
deleteUserManagementCluster
Этот API используется для удаления содержимого кластера UserAccountManagement.
Котлин
client.deleteUserManagementCluster()
Ява
client.deleteUserManagementCluster();
Когда служба получает запрос, она удаляет существующие данные из кластера UserAccountManagement. В случае ошибки весь запрос отклоняется и существующее состояние сохраняется.
deleteClusters
Этот API используется для удаления содержимого кластера определенного типа.
Котлин
client.deleteClusters( DeleteClustersRequest.Builder() .addClusterType(ClusterType.TYPE_CONTINUATION) .addClusterType(ClusterType.TYPE_FEATURED) .addClusterType(ClusterType.TYPE_RECOMMENDATION) .build())
Ява
client.deleteClusters( new DeleteClustersRequest.Builder() .addClusterType(ClusterType.TYPE_CONTINUATION) .addClusterType(ClusterType.TYPE_FEATURED) .addClusterType(ClusterType.TYPE_RECOMMENDATION) .build());
Когда служба получает запрос, она удаляет существующие данные из всех кластеров, соответствующих указанным типам кластеров. Клиенты могут выбрать передачу одного или нескольких типов кластеров. В случае ошибки весь запрос отклоняется и существующее состояние сохраняется.
Обработка ошибок
Настоятельно рекомендуется прослушивать результат задачи из API публикации, чтобы можно было предпринять последующие действия для восстановления и повторной отправки успешной задачи.
Котлин
client.publishRecommendationClusters( PublishRecommendationClustersRequest.Builder() .addRecommendationCluster(..) .build()) .addOnCompleteListener { task -> if (task.isSuccessful) { // do something } else { val exception = task.exception if (exception is AppEngageException) { @AppEngageErrorCode val errorCode = exception.errorCode if (errorCode == AppEngageErrorCode.SERVICE_NOT_FOUND) { // do something } } } }
Ява
client.publishRecommendationClusters( new PublishRecommendationClustersRequest.Builder() .addRecommendationCluster(...) .build()) .addOnCompleteListener( task -> { if (task.isSuccessful()) { // do something } else { Exception exception = task.getException(); if (exception instanceof AppEngageException) { @AppEngageErrorCode int errorCode = ((AppEngageException) exception).getErrorCode(); if (errorCode == AppEngageErrorCode.SERVICE_NOT_FOUND) { // do something } } } });
Ошибка возвращается как AppEngageException
а причина указывается в виде кода ошибки.
Код ошибки | Примечание |
---|---|
SERVICE_NOT_FOUND | Услуга недоступна на данном устройстве. |
SERVICE_NOT_AVAILABLE | Услуга доступна на данном устройстве, но недоступна на момент звонка (например, отключена явно). |
SERVICE_CALL_EXECUTION_FAILURE | Выполнение задачи не удалось из-за проблем с потоками. В этом случае его можно повторить. |
SERVICE_CALL_PERMISSION_DENIED | Вызывающий абонент не имеет права совершать вызов службы. |
SERVICE_CALL_INVALID_ARGUMENT | Запрос содержит недопустимые данные (например, количество кластеров превышает допустимое). |
SERVICE_CALL_INTERNAL | Ошибка на стороне сервиса. |
SERVICE_CALL_RESOURCE_EXHAUSTED | Вызов службы поддержки осуществляется слишком часто. |
Шаг 3. Обработка намерений трансляции
Помимо выполнения вызовов API публикации контента через задание, также необходимо настроить BroadcastReceiver
для получения запроса на публикацию контента.
Целью широковещательных намерений является главным образом повторная активация приложений и принудительная синхронизация данных. Широковещательные намерения не предназначены для частой отправки. Он срабатывает только тогда, когда служба Engage определяет, что контент может быть устаревшим (например, недельной давности). Таким образом, появляется больше уверенности в том, что пользователь сможет получить новый контент, даже если приложение не запускалось в течение длительного периода времени.
BroadcastReceiver
необходимо настроить двумя следующими способами:
- Динамически зарегистрируйте экземпляр класса
BroadcastReceiver
с помощьюContext.registerReceiver()
. Это позволяет осуществлять связь с приложениями, которые все еще находятся в памяти.
Котлин
class AppEngageBroadcastReceiver : BroadcastReceiver(){ // Trigger recommendation cluster publish when PUBLISH_RECOMMENDATION broadcast // is received // Trigger featured cluster publish when PUBLISH_FEATURED broadcast is received // Trigger continuation cluster publish when PUBLISH_CONTINUATION broadcast is // received } fun registerBroadcastReceivers(context: Context){ var context = context context = context.applicationContext // Register Recommendation Cluster Publish Intent context.registerReceiver(AppEngageBroadcastReceiver(), IntentFilter(Intents.ACTION_PUBLISH_RECOMMENDATION)) // Register Featured Cluster Publish Intent context.registerReceiver(AppEngageBroadcastReceiver(), IntentFilter(Intents.ACTION_PUBLISH_FEATURED)) // Register Continuation Cluster Publish Intent context.registerReceiver(AppEngageBroadcastReceiver(), IntentFilter(Intents.ACTION_PUBLISH_CONTINUATION)) }
Ява
class AppEngageBroadcastReceiver extends BroadcastReceiver { // Trigger recommendation cluster publish when PUBLISH_RECOMMENDATION broadcast // is received // Trigger featured cluster publish when PUBLISH_FEATURED broadcast is received // Trigger continuation cluster publish when PUBLISH_CONTINUATION broadcast is // received } public static void registerBroadcastReceivers(Context context) { context = context.getApplicationContext(); // Register Recommendation Cluster Publish Intent context.registerReceiver(new AppEngageBroadcastReceiver(), new IntentFilter(com.google.android.engage.service.Intents.ACTION_PUBLISH_RECOMMENDATION)); // Register Featured Cluster Publish Intent context.registerReceiver(new AppEngageBroadcastReceiver(), new IntentFilter(com.google.android.engage.service.Intents.ACTION_PUBLISH_FEATURED)); // Register Continuation Cluster Publish Intent context.registerReceiver(new AppEngageBroadcastReceiver(), new IntentFilter(com.google.android.engage.service.Intents.ACTION_PUBLISH_CONTINUATION)); }
- Статически объявите реализацию с помощью тега
<receiver>
в файлеAndroidManifest.xml
. Это позволяет приложению получать широковещательные намерения, когда оно не запущено, а также позволяет приложению публиковать контент.
<application>
<receiver
android:name=".AppEngageBroadcastReceiver"
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="com.google.android.engage.action.PUBLISH_RECOMMENDATION" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.engage.action.PUBLISH_FEATURED" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.engage.action.PUBLISH_CONTINUATION" />
</intent-filter>
</receiver>
</application>
Служба отправляет следующие намерения :
-
com.google.android.engage.action.PUBLISH_RECOMMENDATION
При получении этого намерения рекомендуется запуститьpublishRecommendationClusters
. -
com.google.android.engage.action.PUBLISH_FEATURED
При получении этого намерения рекомендуется запуститьpublishFeaturedCluster
. -
com.google.android.engage.action.PUBLISH_CONTINUATION
При получении этого намерения рекомендуется запуститьpublishContinuationCluster
.
Рабочий процесс интеграции
Пошаговое руководство по проверке интеграции после ее завершения см. в разделе Рабочий процесс интеграции с разработчиком .
Часто задаваемые вопросы
См. Часто задаваемые вопросы по Engage SDK .
Контакт
Если в процессе интеграции возникнут вопросы, свяжитесь с нами по адресу Engage-developers@google.com .
Следующие шаги
После завершения интеграции ваши следующие действия будут следующими:
- Отправьте электронное письмо на адрес Engage-developers@google.com и прикрепите интегрированный APK-файл, готовый к тестированию Google.
- Google выполняет внутреннюю проверку и проверку, чтобы убедиться, что интеграция работает должным образом. Если потребуются изменения, Google свяжется с вами и предоставит всю необходимую информацию.
- Когда тестирование будет завершено и никаких изменений не потребуется, Google свяжется с вами и уведомит вас о том, что вы можете начать публикацию обновленного и интегрированного APK в Play Store.
- После того как Google подтвердит, что ваш обновленный APK-файл опубликован в Play Store, ваши кластеры «Рекомендации» , «Избранное» и «Продолжение» могут быть опубликованы и видны пользователям.