Кампании и рекомендации в Android

Сначала подключите SDK и опубликуйте кампанию в приложении. Навигация по URL/deeplink реализуется в приложении через callback.

In-App кампании

SDK автоматически отображает in-app-кампании, если они приходят в ответ на trackView(...) или triggerEvent(...).

Автопоказ поддерживает MODAL, BOTTOM_SHEET, FULL_SCREEN и SNACK_BAR. Ответ должен содержать непустой variables.elements; INLINE в этот flow не выводится. SDK проверяет, что Activity не уничтожена и не завершается, и учитывает presentation lock.

Приложение отвечает за:

  • корректный PageContext;
  • корректный activityContext;
  • обработку действий пользователя через gravityEventCallback. Блокировка новых in-app показов описана в Конфигурации.

GravityInlineView

Используйте GravityInlineView, если экран построен на XML.

<ai.gravityfield.gravity_sdk.ui.GravityInlineView
    android:id="@+id/recommendationsView"
    android:layout_width="match_parent"
    android:layout_height="250dp"
    app:selector="&quot;homepage-recs&quot;"
    app:color="#FFF1F1F1"
    app:cornerRadius="20dp" />

После инфлейта обязательно передайте PageContext:

val inlineView = findViewById<GravityInlineView>(R.id.recommendationsView)
inlineView.init(
    PageContext(
        type = ContextType.HOMEPAGE,
        data = emptyList(),
        location = "app://homepage",
    )
)

Поддерживаемые XML-атрибуты:

Атрибут Описание
app:selector Селектор кампании.
app:color Цвет фона контейнера.
app:cornerRadius Единый радиус скругления.
app:cornerRadiusTopStart Радиус верхнего левого угла.
app:cornerRadiusTopEnd Радиус верхнего правого угла.
app:cornerRadiusBottomStart Радиус нижнего левого угла.
app:cornerRadiusBottomEnd Радиус нижнего правого угла.
app:loaderLayout Кастомный layout для состояния загрузки.

GravityInlineCompose

Используйте GravityInlineCompose, если экран написан на Jetpack Compose.

GravityInlineCompose(
    modifier = Modifier
        .fillMaxWidth()
        .height(250.dp),
    selector = JSONObject.quote("homepage-recs"),
    pageContext = PageContext(
        type = ContextType.HOMEPAGE,
        data = emptyList(),
        location = "app://homepage",
    ),
    loader = { CircularProgressIndicator() },
)

loader - это composable для состояния загрузки. Если placeholder не нужен, можно передать loader = null.

GravityInlineListView

GravityInlineListView используется для отображения нескольких inline-элементов из одной группы.

<ai.gravityfield.gravity_sdk.ui.GravityInlineListView
    android:id="@+id/inlineListView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:groupSelector="&quot;homepage-group&quot;" />
val inlineListView = findViewById<GravityInlineListView>(R.id.inlineListView)
inlineListView.init(
    PageContext(
        type = ContextType.HOMEPAGE,
        data = emptyList(),
        location = "app://homepage",
    )
)

Этот компонент полезен для сценариев, где несколько блоков должны быть загружены одной группой кампаний.

Кэш inline-блоков

GravityInlineView, GravityInlineCompose и GravityInlineListView используют кэш, чтобы восстановить контент и позицию скролла при пересоздании view. Это важно для экранов с RecyclerView, повторным использованием view и несколькими inline-блоками на одном экране.

Кэш привязан к полному ключу:

  • для GravityInlineView и GravityInlineCompose - selector + PageContext;
  • для GravityInlineListView - groupSelector + PageContext.

SDK не очищает inline-кэш автоматически, потому что не знает, закрыт экран окончательно или view будет переиспользована. При закрытии экрана сбросьте кэш самостоятельно:

class InlineDemoActivity : AppCompatActivity() {
    private val pageContext = PageContext(
        type = ContextType.HOMEPAGE,
        data = emptyList(),
        location = "app://homepage",
    )

    override fun onDestroy() {
        if (isFinishing) {
            GravitySDK.instance.resetInlineViewCache(
                selector = JSONObject.quote("homepage-recs"),
                pageContext = pageContext,
            )
        }
        super.onDestroy()
    }
}

Для GravityInlineListView используйте отдельный метод:

GravitySDK.instance.resetInlineListViewCache(
    groupSelector = JSONObject.quote("homepage-group"),
    pageContext = pageContext,
)

Если на экране несколько inline-блоков, сбрасывайте кэш для каждого selector или groupSelector, который использовался на этом экране. Для Fragment/Compose выбирайте момент окончательного выхода из маршрута; уничтожение view при смене конфигурации не всегда означает закрытие экрана. Сброс удаляет данные кэша, но не запускает повторную загрузку уже отображённого view.

Используйте тот же экземпляр значения selector/groupSelector, включая JSON-кодирование, и тот же PageContext, что при загрузке. Отличие любого поля контекста создаёт другой ключ.

Карточки товаров

ProductViewBuilder нужен для отображения карточек товара в блоке рекомендаций. Для карточек в дизайне приложения передайте SDK свой шаблон. Если builder отсутствует, Android использует демонстрационную GravityProduct; не рассчитывайте на неё как на карточку вашего приложения.

SDK получает recommendation-блок, управляет контейнером, списком слотов и tracking-контекстом, но сам шаблон товарной карточки задает приложение. Разработчик реализует ProductViewBuilder или LegacyProductViewBuilder и передает его в GravitySDK.initialize(...) через параметр productViewBuilder.

После этого SDK использует переданный шаблон для каждого товара в product/recommendation-блоках: вызывает builder для каждого slot и передает в него slot, content и campaign. Поэтому карточка отображается как native UI приложения, а не как готовая карточка, нарисованная SDK.

slot содержит данные одного рекомендованного товара:

  • slot.item - словарь с полями товара из фида или из запрошенных fields: например sku, name, price, old_price, image_url, url, brand, currency и другие кастомные поля;
  • slot.strId и slot.slotId - идентификаторы позиции товара в выдаче;
  • slot.fallback - признак, что товар пришел из fallback-логики;
  • slot.events - tracking-данные товара, которые SDK использует при отправке engagement.

Названия ключей в slot.item должны совпадать с вашим фидом и настройкой fields. Например, если в ответе приходит imageUrl, читайте imageUrl; если используется поле фида image_url, читайте image_url.

Важно:

  • SDK автоматически отслеживает visible impression товара во встроенном блоке;
  • клик по товару в кастомной карточке нужно отправлять вручную через sendProductEngagement(...).

Пример шаблона карточки для Jetpack Compose:

Полный пример: product_view_builder.kt. Он использует Coil 3.1.0; зависимости для загрузки изображений. Замените package на package приложения, реализуйте openProduct и передайте MyComposeProductViewBuilder в initialize.

Кто отправляет engagement

Успешная загрузка автоматически отправляет content load tracking. Встроенные UI-компоненты отправляют показы контента, видимость товаров и tracking для поддерживаемых действий. Карточка из ProductViewBuilder уже обёрнута detector SDK: вручную отправляйте её клик перед навигацией, не дублируйте visible impression.

Ручные вызовы engagement не генерируют tracking callback. Для собственного UI используйте отдельные правила аналитики.

Android ждёт видимость не менее 50% в течение секунды; после выхода и возвращения событие может повториться.