# Live Updates на Android

Pushwoosh поддерживает Live Updates на Android через модуль `pushwoosh-liveupdates` (SDK 6.9.0 и новее). Live Update — это непрерывное уведомление в стиле прогресс-бара, которое система выводит на экран блокировки, в панель уведомлений и в виде чипа состояния в строке состояния, чтобы пользователи могли следить за активностью, не открывая ваше приложение.

Весь жизненный цикл управляется с сервера: ваш бэкенд отправляет push-уведомление, когда активность начинается, новые push-уведомления по мере ее продвижения и финальное push-уведомление, когда она заканчивается. SDK отображает каждое из них автоматически.

## Что такое Live Updates

Live Updates были представлены в Android 16 (API 36) как способ отображения инициированной пользователем, чувствительной ко времени активности от начала до конца. Они основаны на уведомлениях платформы, ориентированных на прогресс, и API `Notification.ProgressStyle`. Концептуально они являются аналогом Live Activities в iOS.

Эта страница описывает только интеграцию с Pushwoosh. Для получения информации о поведении платформы, правилах продвижения и рекомендациях по дизайну обратитесь к официальной документации Android:

- [Уведомления, ориентированные на прогресс](https://developer.android.com/about/versions/16/features/progress-centric-notifications)
- [Создание интерактивных уведомлений (Views)](https://developer.android.com/develop/ui/views/notifications/live-update)
- [Создание интерактивных уведомлений (Compose)](https://developer.android.com/develop/ui/compose/notifications/live-update)

## Когда использовать Live Updates

Live Updates предназначены для непрерывной, инициированной пользователем и чувствительной ко времени активности — чего-то с четким началом и концом, что важно пользователю прямо сейчас. Типичные сценарии для клиентов Pushwoosh:

- **Доставка еды** — заказ принят, готовится, в пути, прибывает.
- **Вызов такси** — водитель назначен, в пути, прибывает, поездка в процессе.
- **Отслеживание заказов и посылок** — актуальный статус заказа, который находится в пути.
- **Спортивные трансляции и медиа** — счет и время матча в реальном времени.
- **Фитнес** — активная тренировка или пробежка с отображением прошедшего времени и прогресса.
- **Финтех** — транзакция или процесс верификации, проходящий через свои этапы.

Поскольку жизненный цикл управляется реальными событиями, о которых ваш бэкенд уже знает (статус заказа меняется, курьер движется), Live Update обычно представляет собой один вызов API, встроенный в ваш существующий поток событий, а не что-то, что человек отправляет вручную.

<Aside type="caution">
Не используйте Live Updates для рекламных акций, сообщений в чате или фоновой информации, и никогда не отправляйте повторно Live Update, которое пользователь отклонил. Android может отозвать у приложения право на публикацию продвигаемых уведомлений. Следуйте [рекомендациям Android по надлежащему использованию](https://developer.android.com/develop/ui/views/notifications/live-update#best-practices).
</Aside>

## Требования

- Android 16 (API 36) или новее. На старых устройствах модуль остается неактивным, и каждый вызов API Live Update является безопасной пустой операцией (no-op).
- Pushwoosh Android SDK 6.9.0 или новее.

## Добавьте модуль pushwoosh-liveupdates

Добавьте зависимость в ваш файл **app/build.gradle**:

```groovy
dependencies {
    implementation 'com.pushwoosh:pushwoosh-liveupdates:<latest-version>'
}
```

Замените `<latest-version>` на текущую версию из [Maven Central](https://mvnrepository.com/artifact/com.pushwoosh/pushwoosh-liveupdates).

Модуль обнаруживается автоматически при запуске. Он объявляет необходимое разрешение `POST_PROMOTED_NOTIFICATIONS`, регистрирует собственный канал уведомлений и перехватывает push-уведомления Live Update до стандартного пути обработки уведомлений. Дополнительный код инициализации не требуется — SDK устанавливает флаги `ongoing` и `promoted`, загружает большую иконку, сопоставляет кнопки действий и публикует уведомление за вас.

## Отправка Live Update

Вы отправляете Live Updates через [Messaging API v2](/ru/developer/api-reference/messaging-api-v2/), добавляя объект `live_update` в блок контента `android`. Используйте `transactional` запрос — Live Update нацелен на конкретного пользователя, чью активность он отслеживает. Поле `schedule` является обязательным; `{ "after": "0s" }` отправляет немедленно. Жизненный цикл имеет три операции, устанавливаемые в `live_update.op`:

- `OPERATION_START` — первое push-уведомление для активности. Публикует непрерывное уведомление.
- `OPERATION_UPDATE` — последующее push-уведомление для той же активности. Обновляет его на месте беззвучно.
- `OPERATION_END` — завершающее push-уведомление. Отклоняет уведомление.

Все push-уведомления, относящиеся к одной и той же активности, должны иметь одинаковый `live_update.id`. Этот идентификатор связывает обновления вместе, а также используется для отклонения обновления из приложения.

Каждое push-уведомление полностью описывает уведомление — ничего не переносится из предыдущего. Повторно отправляйте каждое поле, которое хотите сохранить, например, сегменты и большую иконку, с каждым `OPERATION_UPDATE`; пропущенное поле будет отображаться как отсутствующее.

### Параметры Live Update

Эти ключи помещаются внутрь объекта `live_update` в блоке контента `android`. Заголовок, текст и большая иконка используют стандартные поля Android push-уведомлений (`title`, `body`, `custom_icon`), отправляемые вместе с `live_update`.

| Параметр | Тип | Описание |
|---|---|---|
| `op` | string | Операция жизненного цикла: `OPERATION_START`, `OPERATION_UPDATE` или `OPERATION_END`. Обязательно. |
| `id` | string | Стабильный идентификатор активности, общий для всех push-уведомлений одного Live Update. Обязательно. |
| `progress` | int | Значение прогресса, измеряемое относительно суммарной длины сегментов. |
| `progress_indeterminate` | bool | Показывать неопределенную анимацию вместо конкретного значения. |
| `progress_bar` | bool | Показывать ли прогресс-бар. По умолчанию `true`. |
| `segments` | array | Упорядоченные сегменты прогресса, каждый в формате `{ "color": "#RRGGBB", "length": N }`. |
| `extras` | object | Произвольные данные, передаваемые в кастомный провайдер стилей. |
| `when` | int64 | Временная метка заголовка в миллисекундах эпохи. |
| `chronometer` | bool | Показывать время в заголовке как работающий таймер. |
| `chronometer_count_down` | bool | Работающий таймер ведет обратный отсчет, а не прямой. |
| `show_when` | bool | Показывать ли столбец времени в заголовке. По умолчанию `true`. |

<Aside>
Значение `op` должно быть одним из точных имен перечисления: `OPERATION_START`, `OPERATION_UPDATE` или `OPERATION_END` — сокращенные формы игнорируются. Каждое поле имеет свой нативный тип JSON: `segments` — это JSON-массив, а `extras` — JSON-объект, а не закодированные строки.
</Aside>

Четыре поля времени комбинируются следующим образом: если `show_when` установлено в `false`, время скрыто; в противном случае `when` является точкой отсчета, `chronometer` превращает его в работающий счетчик, а `chronometer_count_down` заставляет этот счетчик идти в обратном направлении.

### Стартовое push-уведомление

```bash
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transactional": {
      "application": "XXXXX-XXXXX",
      "platforms": ["ANDROID"],
      "users": { "list": ["customer-42"] },
      "payload": {
        "content": {
          "localized_content": {
            "default": {
              "android": {
                "title": "Order #4521",
                "body": "We are preparing your order",
                "custom_icon": "https://example.com/restaurant.png",
                "live_update": {
                  "op": "OPERATION_START",
                  "id": "order_4521",
                  "progress": 1,
                  "segments": [
                    { "color": "#34A853", "length": 3 },
                    { "color": "#FBBC05", "length": 4 },
                    { "color": "#4285F4", "length": 3 }
                  ],
                  "extras": { "eta": "18:40" }
                }
              }
            }
          }
        }
      },
      "schedule": { "after": "0s" },
      "message_type": "MESSAGE_TYPE_TRANSACTIONAL"
    }
  }'
```

### Обновляющее push-уведомление

Отправляйте `OPERATION_UPDATE` с тем же `id` каждый раз, когда активность продвигается. Повторяйте сегменты и иконку — обновление, в котором они отсутствуют, будет отображено без них.

```bash
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transactional": {
      "application": "XXXXX-XXXXX",
      "platforms": ["ANDROID"],
      "users": { "list": ["customer-42"] },
      "payload": {
        "content": {
          "localized_content": {
            "default": {
              "android": {
                "title": "Order #4521",
                "body": "Your courier is on the way",
                "custom_icon": "https://example.com/restaurant.png",
                "live_update": {
                  "op": "OPERATION_UPDATE",
                  "id": "order_4521",
                  "progress": 7,
                  "segments": [
                    { "color": "#34A853", "length": 3 },
                    { "color": "#FBBC05", "length": 4 },
                    { "color": "#4285F4", "length": 3 }
                  ]
                }
              }
            }
          }
        }
      },
      "schedule": { "after": "0s" },
      "message_type": "MESSAGE_TYPE_TRANSACTIONAL"
    }
  }'
```

### Завершающее push-уведомление

Завершающему push-уведомлению нужны только операция и идентификатор.

```bash
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transactional": {
      "application": "XXXXX-XXXXX",
      "platforms": ["ANDROID"],
      "users": { "list": ["customer-42"] },
      "payload": {
        "content": {
          "localized_content": {
            "default": {
              "android": {
                "live_update": {
                  "op": "OPERATION_END",
                  "id": "order_4521"
                }
              }
            }
          }
        }
      },
      "schedule": { "after": "0s" },
      "message_type": "MESSAGE_TYPE_TRANSACTIONAL"
    }
  }'
```

## Кастомизация внешнего вида

SDK поставляется с дефолтным стилем прогресса, построенным из `progress`, `progress_indeterminate` и `segments`. Чтобы получить полный контроль над прогресс-баром, реализуйте `LiveUpdateProgressStyleProvider` и самостоятельно сформируйте `Notification.ProgressStyle`.

Провайдер — это единственная точка кастомизации. SDK по-прежнему управляет настройкой канала, флагами `ongoing` и `promoted`, большой иконкой, кнопками действий и временем в заголовке — кастомный провайдер может формировать только прогресс-бар, поэтому он не может нарушить право на продвижение. Он должен быть без состояния (stateless): возвращаемый стиль должен быть получен только из предоставленного `LiveUpdateState`. Если он выбрасывает исключение, SDK возвращается к дефолтному стилю, и уведомление все равно публикуется.

<Tabs>
<TabItem label="Java">
```java
import android.app.Notification;
import androidx.annotation.NonNull;
import com.pushwoosh.liveupdates.LiveUpdateProgressStyleProvider;
import com.pushwoosh.liveupdates.LiveUpdateSegment;
import com.pushwoosh.liveupdates.LiveUpdateState;
import java.util.List;

public class OrderStyleProvider implements LiveUpdateProgressStyleProvider {
    @NonNull
    @Override
    public Notification.ProgressStyle createStyle(@NonNull LiveUpdateState state) {
        Notification.ProgressStyle style = new Notification.ProgressStyle();
        if (state.getProgress() != null) {
            style.setProgress(state.getProgress());
        }
        style.setProgressIndeterminate(state.isProgressIndeterminate());

        List<LiveUpdateSegment> segments = state.getSegments();
        int boundary = 0;
        for (int i = 0; i < segments.size(); i++) {
            LiveUpdateSegment seg = segments.get(i);
            style.addProgressSegment(
                new Notification.ProgressStyle.Segment(seg.getLength()).setColor(seg.getColor()));
            boundary += seg.getLength();
            if (i < segments.size() - 1) {
                style.addProgressPoint(new Notification.ProgressStyle.Point(boundary));
            }
        }
        return style;
    }
}
```
</TabItem>
<TabItem label="Kotlin">
```kotlin
import android.app.Notification
import com.pushwoosh.liveupdates.LiveUpdateProgressStyleProvider
import com.pushwoosh.liveupdates.LiveUpdateState

class OrderStyleProvider : LiveUpdateProgressStyleProvider {
    override fun createStyle(state: LiveUpdateState): Notification.ProgressStyle {
        val style = Notification.ProgressStyle()
        state.progress?.let { style.setProgress(it) }
        style.setProgressIndeterminate(state.isProgressIndeterminate)

        val segments = state.segments
        var boundary = 0
        segments.forEachIndexed { i, seg ->
            style.addProgressSegment(
                Notification.ProgressStyle.Segment(seg.length).setColor(seg.color))
            boundary += seg.length
            if (i < segments.size - 1) {
                style.addProgressPoint(Notification.ProgressStyle.Point(boundary))
            }
        }
        return style
    }
}
```
</TabItem>
</Tabs>

Зарегистрируйте провайдер с помощью тега `<meta-data>` в **AndroidManifest.xml**. Класс должен иметь публичный конструктор без аргументов.

```xml
<meta-data
    android:name="com.pushwoosh.LIVE_UPDATE_STYLE_PROVIDER"
    android:value="com.example.OrderStyleProvider" />
```

Используйте `LiveUpdateState.getExtras()` для чтения JSON, который вы отправили в `live_update.extras`, и адаптируйте стиль к вашим собственным бизнес-данным.

## Управление Live Updates из вашего приложения

Сервер управляет каждым `OPERATION_START`, `OPERATION_UPDATE` и `OPERATION_END`, поэтому нет API на стороне приложения для публикации или обновления Live Update. Фасад `PushwooshLiveUpdates` охватывает только то, что не может сделать сервер — локальное отклонение обновления и проверка, какие из них отображаются на экране.

```java
import com.pushwoosh.liveupdates.PushwooshLiveUpdates;

// Отклонить конкретный Live Update, когда пользователь завершает активность в приложении,
// не дожидаясь завершающего push-уведомления "end" от сервера
PushwooshLiveUpdates.endLiveUpdate("order_4521");

// Получить список идентификаторов активностей, отображаемых в данный момент этим приложением
List<String> active = PushwooshLiveUpdates.getActiveIds();

// Очистить все, что отображает это приложение, например, при выходе из системы
PushwooshLiveUpdates.endAllLiveUpdates();
```

Все методы безопасны для вызова из любого потока и являются пустой операцией (no-op) на устройствах ниже Android 16.

## Ссылки по теме

- [Обзор Pushwoosh Android SDK](/ru/developer/pushwoosh-sdk/android-sdk/)
- [Справочник по API Pushwoosh Android SDK](https://pushwoosh.github.io/pushwoosh-android-sdk/)
- [Messaging API](/ru/developer/api-reference/messaging-api-v2/)
- [Уведомления Android, ориентированные на прогресс](https://developer.android.com/about/versions/16/features/progress-centric-notifications)