# Live Updates on Android

Pushwoosh supports Android Live Updates through the `pushwoosh-liveupdates` module (SDK 6.9.0 and later). A Live Update is an ongoing, progress-style notification that the system promotes on the lock screen, in the notification drawer, and as a status chip in the status bar, so users can follow an activity without opening your app.

The whole lifecycle is driven from the server: your backend sends a push when the activity starts, more pushes as it progresses, and a final push when it ends. The SDK renders each one automatically.

## What are Live Updates

Live Updates were introduced in Android 16 (API 36) as a way to surface a user-initiated, time-sensitive activity from start to finish. They build on the platform's progress-centric notifications and `Notification.ProgressStyle` API. Conceptually they are the Android counterpart of iOS Live Activities.

This page covers the Pushwoosh integration only. For the platform behavior, promotion rules, and design guidance, see the official Android documentation:

- [Progress-centric notifications](https://developer.android.com/about/versions/16/features/progress-centric-notifications)
- [Create live update notifications (Views)](https://developer.android.com/develop/ui/views/notifications/live-update)
- [Create live update notifications (Compose)](https://developer.android.com/develop/ui/compose/notifications/live-update)

## When to use Live Updates

Live Updates are meant for an activity that is ongoing, user-initiated, and time-sensitive — something with a clear start and end that the user actively cares about right now. Typical scenarios for Pushwoosh customers:

- **Food delivery** — order accepted, being prepared, out for delivery, arriving.
- **Ride-hailing and taxi** — driver assigned, en route, arriving, trip in progress.
- **Order and shipment tracking** — live status of an order that is actively in transit.
- **Live sports and media** — match score and time as the game unfolds.
- **Fitness** — an active workout or run with elapsed time and progress.
- **Fintech** — a transaction or verification flow moving through its stages.

Because the lifecycle is driven by real events that your backend already knows about (an order changes status, a courier moves), a Live Update is usually one API call wired into your existing event flow — not something a person sends by hand.

<Aside type="caution">
Do not use Live Updates for promotions, chat messages, or ambient information, and never repost a Live Update the user has dismissed. Android may revoke the app's ability to post promoted notifications. Follow the [Android guidance on appropriate use](https://developer.android.com/develop/ui/views/notifications/live-update#best-practices).
</Aside>

## Requirements

- Android 16 (API 36) or newer. On older devices the module stays inactive and every Live Update API call is a safe no-op.
- Pushwoosh Android SDK 6.9.0 or later.

## Add the pushwoosh-liveupdates module

Add the dependency to your **app/build.gradle**:

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

Replace `<latest-version>` with the current version from [Maven Central](https://mvnrepository.com/artifact/com.pushwoosh/pushwoosh-liveupdates).

The module is discovered automatically at startup. It declares the required `POST_PROMOTED_NOTIFICATIONS` permission, registers its own notification channel, and intercepts Live Update pushes before the default notification path. There is no extra initialization code — the SDK sets the ongoing and promoted flags, downloads the large icon, maps action buttons, and posts the notification for you.

## Send a Live Update

You send Live Updates through [Messaging API v2](/developer/api-reference/messaging-api-v2/) by adding a `live_update` object to the `android` content block. Use a `transactional` request — a Live Update targets the specific user whose activity it tracks. The `schedule` field is required; `{ "after": "0s" }` sends immediately. The lifecycle has three operations, set in `live_update.op`:

- `OPERATION_START` — first push for an activity. Posts the ongoing notification.
- `OPERATION_UPDATE` — a later push for the same activity. Refreshes it in place, silently.
- `OPERATION_END` — terminal push. Dismisses the notification.

All pushes that belong to the same activity must share the same `live_update.id`. That id ties the updates together and is also what you use to dismiss the update from the app.

Each push fully describes the notification — nothing carries over from the previous push. Resend every field you want to keep, such as the segments and the large icon, with each `OPERATION_UPDATE`; an omitted field is rendered as absent.

### Live Update parameters

These keys go inside the `live_update` object of the `android` content block. Title, body, and large icon use the standard Android push fields (`title`, `body`, `custom_icon`), sent alongside `live_update`.

| Parameter | Type | Description |
|---|---|---|
| `op` | string | Lifecycle operation: `OPERATION_START`, `OPERATION_UPDATE`, or `OPERATION_END`. Required. |
| `id` | string | Stable activity id shared by all pushes of one Live Update. Required. |
| `progress` | int | Progress value, measured against the summed segment lengths. |
| `progress_indeterminate` | bool | Show an indeterminate animation instead of a concrete value. |
| `progress_bar` | bool | Show the progress bar at all. Defaults to `true`. |
| `segments` | array | Ordered progress segments, each `{ "color": "#RRGGBB", "length": N }`. |
| `extras` | object | Arbitrary data passed through to a custom style provider. |
| `when` | int64 | Header time anchor, in epoch milliseconds. |
| `chronometer` | bool | Show the header time as a running timer. |
| `chronometer_count_down` | bool | A running timer counts down instead of up. |
| `show_when` | bool | Show the header time column at all. Defaults to `true`. |

<Aside>
The `op` value must be one of the exact enum names `OPERATION_START`, `OPERATION_UPDATE`, or `OPERATION_END` — short forms are ignored. Each field carries its native JSON type: `segments` is a JSON array and `extras` a JSON object, not encoded strings.
</Aside>

The four time fields combine as follows: with `show_when` set to `false` the time is hidden; otherwise `when` is the anchor, `chronometer` turns it into a live counter, and `chronometer_count_down` makes that counter run backwards.

### Start 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"
    }
  }'
```

### Update push

Send an `OPERATION_UPDATE` with the same `id` whenever the activity moves forward. Repeat the segments and the icon — an update that omits them renders without them.

```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"
    }
  }'
```

### End push

The terminal push only needs the operation and the 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": {
                "live_update": {
                  "op": "OPERATION_END",
                  "id": "order_4521"
                }
              }
            }
          }
        }
      },
      "schedule": { "after": "0s" },
      "message_type": "MESSAGE_TYPE_TRANSACTIONAL"
    }
  }'
```

## Customize the appearance

The SDK ships a default progress style built from `progress`, `progress_indeterminate`, and `segments`. To take full control of the progress bar, implement `LiveUpdateProgressStyleProvider` and shape a `Notification.ProgressStyle` yourself.

The provider is the single customization point. The SDK still owns channel setup, the ongoing and promoted flags, the large icon, action buttons, and the header time — a custom provider can only shape the progress bar, so it cannot break promotion eligibility. It must be stateless: derive the returned style only from the supplied `LiveUpdateState`. If it throws, the SDK falls back to the default style and the notification still posts.

<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>

Register the provider with a `<meta-data>` tag in **AndroidManifest.xml**. The class must have a public no-argument constructor.

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

Use `LiveUpdateState.getExtras()` to read the JSON you sent in `live_update.extras` and adapt the style to your own business data.

## Manage Live Updates from your app

The server drives every `OPERATION_START`, `OPERATION_UPDATE`, and `OPERATION_END`, so there is no app-side API to post or refresh a Live Update. The `PushwooshLiveUpdates` facade covers only what the server cannot do — dismissing an update locally and checking which ones are on screen.

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

// Dismiss a specific Live Update when the user finishes the activity in-app,
// without waiting for the server's terminal "end" push
PushwooshLiveUpdates.endLiveUpdate("order_4521");

// List the activity ids currently shown by this app
List<String> active = PushwooshLiveUpdates.getActiveIds();

// Clear everything this app is showing, for example on logout
PushwooshLiveUpdates.endAllLiveUpdates();
```

All methods are safe to call from any thread and are a no-op on devices below Android 16.

## Related links

- [Pushwoosh Android SDK overview](/developer/pushwoosh-sdk/android-sdk/)
- [Pushwoosh Android SDK API reference](https://pushwoosh.github.io/pushwoosh-android-sdk/)
- [Messaging API](/developer/api-reference/messaging-api-v2/)
- [Android progress-centric notifications](https://developer.android.com/about/versions/16/features/progress-centric-notifications)