Backend-Driven UI: рекомендации в Android

Используйте этот сценарий, когда приложение само рисует виджет, а сервер управляет его структурой, стилем и подборкой товаров. Загрузите контент по selector, сохраните campaign/content/slot для engagement и передайте структуру собственному рендереру. Общие правила загрузки.

Структура виджета

Поле Как использовать
response.data[*].payload[*].contents[*] Выберите блок кампании, который будет отображаться
content.variables?.frameUI?.container Внешний контейнер: фон, скругление, padding, margin, size и onClick
content.variables?.elements Элементы в порядке, заданном сервером
products-container Место вставки товарного блока
element.style.productContainerType Layout row или grid
element.style.gridColumns Число колонок; если отсутствует, используйте дефолт клиента
content.products?.slots Товары в порядке ранжирования сервера
slot.item Поля товара из вашего фида

products.strategyId, products.name и products.fallback описывают выбранную стратегию. Сохраняйте порядок товаров и элементов; не подменяйте серверный заголовок или layout локальными значениями.

Поддерживаемые элементы

Элемент / действие Встроенный SDK Рендерер из примера
text, image, button, spacer Поддерживает Поддерживает
products-container, row/grid Поддерживает через ProductViewBuilder Рисует собственные карточки
items-container, webview Поддерживает Требует дополнительной реализации
URL, deeplink, copy Поддерживает; навигация передаётся в callback Обрабатывает через переданные функции и clipboard
OPEN_STEP Переключает шаг кампании Требует дополнительной реализации

Пример применяет backgroundColor, cornerRadius, padding, margin, size, fontSize, fontWeight, textColor, contentAlignment, src, fit и параметры товарного layout. Добавляйте новые элементы и действия явно; для неизвестных типов определите безопасный fallback. frameUI.container.onClick можно подключить к wrapper через тот же обработчик действий, что для element.onClick.

Подключите полный пример

Скачайте server_driven_widget.kt и добавьте файл в проект с SDK. Кампания для примера содержит один корневой блок без дополнительных шагов. Передайте обычное имя selector, PageContext и обработчики открытия URL, deeplink и товара.

Compose-пример использует package com.example.gravitydemo; замените его на package приложения. Для изображений добавьте зависимости:

dependencies {
    implementation("io.coil-kt.coil3:coil-compose:3.1.0")
    implementation("io.coil-kt.coil3:coil-network-okhttp:3.1.0")
}
ServerDrivenRecommendationWidget(
    selector = "homepage-recommendations",
    pageContext = pageContext,
    onOpenUrl = ::openUrl,
    onOpenDeeplink = ::openDeeplink,
    onOpenProduct = ::openProduct,
)

Внутри примера selector кодируется через JSONObject.quote один раз. Уже закодированное значение передавать не нужно.

Аналитика и ограничения

Пример вручную отправляет impression после появления виджета, visible impression — при видимости минимум 50% в течение секунды, клик товара — до навигации. Видимые показы дедуплицируются для размещения. Detector сравнивает геометрию с viewport и не учитывает перекрывающие слои.

Не передавайте decisionId или tracking URL отдельно: используйте campaign/content/slot из ответа. Ручной engagement не вызывает gravityEventCallback. Типы engagement и условия отправки.

Проверка

  1. Переключите row/grid, заголовок и порядок elements в кампании: после новой загрузки UI должен использовать серверную конфигурацию.
  2. Прокрутите виджет, нажмите товар и проверьте engagement без дублирования из callback.
  3. Проверьте пустой ответ, ошибку загрузки и отсутствующие поля: виджет должен безопасно скрыться.
  4. Не добавляйте неподдерживаемые элементы или дополнительные шаги без расширения рендерера.