# Android Live Updates

Add and manage Android Live Updates using the push API. {{< badge "axp" >}}

For SDK methods, see the [Live Updates](https://www.airship.com/docs/sdk-topics/live-updates/) developer documentation. See also the [Android Live Updates](https://www.airship.com/docs/guides/features/messaging/live-activities-updates/) feature guide.

## About implementation

Live Updates can be started, updated, and ended using API push notifications or the SDK. The notification, widget, or custom app view refreshes with the information contained in the latest Live Update received by the device.

First you must implement a handler and define its type. This determines what occurs in the app when it receives a Live Update. Then register the handler with the Airship SDK so the handler can be notified upon receiving a `live_update` payload. Then start the Live Update by sending a push or implementing custom code. When you start the Live Update, you define its name in the `live_update` object. You can update and end by sending additional pushes or using the `LiveUpdateManager`.

* Types must be unique for each `LiveUpdateHandler` defined by an app.
* A handler can handle multiple uniquely named Live Updates.
* A name can be unique for a [Channel ID](https://www.airship.com/docs/reference/glossary/#channel_id) (e.g., `order-12345`) or shared across multiple channel IDs (e.g., `sports-game-123`).
* A name can be reused after its Live Update has ended.

For example, to provide updates for tracking a sports game, start a Live Update with name `sports-game-123`. The app will track and display changes through the Airship SDK using that name.

## Creating a handler

The Airship SDK supports two types of Live Update handlers:

* `NotificationLiveUpdateHandler` — Displays a notification with a custom layout, with content updated by the Live Update.
* `CustomLiveUpdateHandler` — Receives Live Update events and provides flexibility to display content using a custom implementation. This can be used to power home screen widgets, views embedded in the app, and more.

Each handler type has two different interfaces that may be implemented, to support suspending or callback-based code:

* `SuspendLiveUpdateNotificationHandler`
* `CallbackLiveUpdateNotificationHandler`
* `SuspendLiveUpdateCustomHandler`
* `CallbackLiveUpdateCustomHandler`

The following `SampleLiveUpdateHandler` reads content from the Live Update payload and displays scores for a sports game in a custom notification layout, using `RemoteViews`:

```kotlin
class SampleLiveUpdateHandler : SuspendLiveUpdateNotificationHandler() {
    override suspend fun onUpdate(
        context: Context,
        event: LiveUpdateEvent,
        update: LiveUpdate
    ): LiveUpdateResult<NotificationCompat.Builder> {

        // Read content_state fields from the Live Update payload
        val teamOneScore = update.content.opt("team_one_score").getInt(0).toString()
        val teamTwoScore = update.content.opt("team_two_score").getInt(0).toString()
        val statusUpdate = update.content.opt("status_update").optString()

        // Expanded notification layout
        val bigLayout = RemoteViews(context.packageName, R.layout.sports_big).apply {
            setTextViewText(R.id.teamOneScore, teamOneScore)
            setTextViewText(R.id.teamTwoScore, teamTwoScore)
            setTextViewText(R.id.statusUpdate, statusUpdate)
        }

        // Collapsed notification layout
        val smallLayout = RemoteViews(context.packageName, R.layout.sports_small).apply {
            setTextViewText(R.id.teamOneScore, teamOneScore)
            setTextViewText(R.id.teamTwoScore, teamTwoScore)
        }

        // Create the notification builder
        val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setCategory(NotificationCompat.CATEGORY_EVENT)
            .setStyle(NotificationCompat.DecoratedCustomViewStyle())
            .setCustomContentView(smallLayout)
            .setCustomBigContentView(bigLayout)

        // Return 'ok' with the notification builder.
        // The Airship SDK will handle posting the notification.
        // Returning LiveUpdateResult.cancel() will end the Live Update and dismiss the notification.
        return LiveUpdateResult.ok(builder)
    }

    companion object {
        private const val NOTIFICATION_CHANNEL_ID = "sports"
    }
}
```


## Registering a handler

Handlers must be registered with `LiveUpdateManager` in order to receive Live Update events. This should be done *once* after `takeOff`.


#### Android Kotlin



In your `Autopilot` class, or in `Application.onCreate`, register the types.

```kotlin
class SampleAutopilot : Autopilot() {

    override fun onAirshipReady(airship: UAirship) {

        LiveUpdateManager.shared().run {
            register(type = "notification", handler = SampleLiveUpdateHandler())
        }
    }
}
```



#### React Native



Using the [AirshipPluginExtender](https://www.airship.com/docs/developer/sdk-integration/react-native/installation/extending-airship/), register the types.

```kotlin
@Keep
public final class AirshipExtender: AirshipPluginExtender {

    override fun onAirshipReady(context: Context, airship: UAirship) {
         LiveUpdateManager.shared().run {
            register(type = "notification", handler = SampleLiveUpdateHandler())
        }
    }

}
```



#### Capacitor



Using the [AirshipPluginExtender](https://www.airship.com/docs/developer/sdk-integration/capacitor/installation/extending-airship/), register the types.

```kotlin
@Keep
public final class AirshipExtender: AirshipPluginExtender {

    override fun onAirshipReady(context: Context, airship: UAirship) {
         LiveUpdateManager.shared().run {
            register(type = "notification", handler = SampleLiveUpdateHandler())
        }
    }

}
```



#### Flutter



Using the [AirshipPluginExtender](https://www.airship.com/docs/developer/sdk-integration/flutter/installation/extending-airship/), register the types.

```kotlin
@Keep
public final class AirshipExtender: AirshipPluginExtender {

    override fun onAirshipReady(context: Context, airship: UAirship) {
         LiveUpdateManager.shared().run {
            register(type = "notification", handler = SampleLiveUpdateHandler())
        }
    }

}
```




> **Note:** The `type` used above, `"notification"`, is used to map Live Update events to the corresponding handler in your app.
> The value can be any string that is unique across all handlers registered by an app. This also allows a single handler to manage multiple Live Updates that each have a unique `name`.


## Starting Live Updates

Once a handler has been registered, Live Updates can be started via the [Push API](https://www.airship.com/docs/developer/rest-api/ua/operations/push/#sendpush) by specifying a [`live_update` payload](https://www.airship.com/docs/developer/rest-api/ua/schemas/platform-overrides/#androidoverrideobject). In this example, a Live Update will be started for all Android devices that have the tag `"sports-scores"`:

```json
{
    "device_types": [
        "android"
    ],
    "audience": {
        "tag": "sports-scores" 
    },
    "notification": {
        "android": {
            "live_update": {
                "name": "sports-game-123",
                "type": "notification",
                "event": "start",
                "content_state": {
                    "team_one_score": 0,
                    "team_two_score": 0,
                    "status_update": "Game started!"
                }
            }
        }
    }
}
```


Live Updates can also be started from within the app.


#### Android Kotlin


```kotlin
LiveUpdateManager.shared().start(
    name = "sports-game-123",
    type = "notification", 
    content = jsonMapOf(
        "team_one_score" to 0,
        "team_two_score" to 0,
        "status_update" to "Game started!"
    )
)
```



#### React Native



```typescript
Airship.android.liveUpdateManager.start({
  name: 'sports-game-123'
  type: 'notification',
  content: {
    team_one_score: 0,
    team_two_score: 0,
    status_update: 'Game started!'
  }
});
```




#### Capacitor



```typescript
Airship.android.liveUpdateManager.start({
  name: 'sports-game-123'
  type: 'notification',
  content: {
    team_one_score: 0,
    team_two_score: 0,
    status_update: 'Game started!'
  }
});
```




#### Flutter



```dart
if (Platform.isAndroid) {
  LiveUpdateStartRequest createRequest = LiveUpdateStartRequest(
    name: "sports-game-123",
    type: 'notification',
    content: {
        'team_one_score': 0,
        'team_two_score': 0,
        'status_update': 'Game started!'
    }
  );

  await Airship.liveUpdateManager.start(createRequest);
}
```





## Updating Live Updates

Now that the Live Update is started, the content can be updated via the [Push API](https://www.airship.com/docs/developer/rest-api/ua/operations/push/#sendpush) with the following payload. This example will send an update to all Android channels that have a Live Update named `sports-game-123`. The `audience` field can be specified to restrict who receives the update.

```json
{
    "device_types": [
        "android"
    ],
    "audience": "all",
    "notification": {
        "android": {
            "live_update": {
                "name": "sports-game-123",
                "event": "update",
                "content_state": {
                    "team_one_score": 3,
                    "team_two_score": 0
                }
            }
        }
    }
}
```




Live Updates can also be updated from within the app.


#### Android Kotlin


```kotlin
LiveUpdateManager.shared().update(
    name = "sports-game-123",
    content = jsonMapOf(
        "team_one_score" to 3,
        "team_two_score" to 0,
        "status_update" to "Game started!"
    )
)
```



#### React Native



```typescript
Airship.android.liveUpdateManager.update({
  name: 'sports-game-123'
  content: {
    team_one_score: 3,
    team_two_score: 0,
    status_update: 'Game started!'
  }
});
```




#### Capacitor



```typescript
Airship.android.liveUpdateManager.update({
  name: 'sports-game-123'
  content: {
    team_one_score: 3,
    team_two_score: 0,
    status_update: 'Game started!'
  }
});
```




#### Flutter



```dart
if (Platform.isAndroid) {
  List<LiveUpdate> updates = await Airship.liveUpdateManager.listAll();

  LiveUpdateUpdateRequest request = LiveUpdateUpdateRequest(
    name: "sports-game-123",
    content: {
        'team_one_score': 0,
        'team_two_score': 0,
        'status_update': 'Game started!'
    }
  );

  await Airship.liveUpdateManager.update(request);
}
```






## Ending Live Updates

To end a Live Update via the [Push API](https://www.airship.com/docs/developer/rest-api/ua/operations/push/#sendpush), send the following payload:

```json
{
    "device_types": [
        "android"
    ],
    "audience": "all",
    "notification": {
        "android": {
            "live_update": {
                "name": "sports-game-123",
                "event": "end",
                "content_state": {
                    "team_one_score": 9,
                    "team_two_score": 6,
                    "status_update": "Game over!"
                }
            }
        }
    }
}
```




#### Android Kotlin


```kotlin
LiveUpdateManager.shared().stop(
    name = "sports-game-123",
    content = jsonMapOf(
        "team_one_score" to 9,
        "team_two_score" to 6,
        "status_update" to "Game over!"
    )
)
```



#### React Native



```typescript
Airship.android.liveUpdateManager.end({
  name: 'sports-game-123'
  content: {
    team_one_score: 9,
    team_two_score: 6,
    status_update: 'Game over!'
  }
});
```




#### Capacitor



```typescript
Airship.android.liveUpdateManager.end({
  name: 'sports-game-123'
  content: {
    team_one_score: 9,
    team_two_score: 6,
    status_update: 'Game over!'
  }
});
```




#### Flutter



```dart
if (Platform.isAndroid) {
  List<LiveUpdate> updates = await Airship.liveUpdateManager.listAll();

  LiveUpdateEndRequest stopRequest = LiveUpdateEndRequest(
    name: "sports-game-123"
  );

  await Airship.liveUpdateManager.end(stopRequest);
}
```





## Clearing all active Live Updates

During development, it can be useful to reset Live Update tracking on app launch. This allows any Live Updates to be started fresh, even if they were already started during a previous launch. To end all currently active Live Updates, call the `clearAll()` method on `LiveUpdateManager`.
 

#### Android Kotlin


```kotlin
LiveUpdateManager.shared().clearAll()
```



#### React Native


```typescript
Airship.android.liveUpdateManager.clearAll();
```



#### Capacitor


```typescript
Airship.android.liveUpdateManager.clearAll();
```



#### Flutter


```dart
if (Platform.isAndroid) {
  await Airship.liveUpdateManager.clearAll();
}
```



