Live Updates for the Android SDK

Integrate Live Updates into your Android app to display real-time updates in notifications, widgets, or within your app. AXP

For information about Live Updates, including overview, use cases, and how to send Live Updates via the Push API, see Android Live Updates.

Installation

Include the Live Updates module in your app’s dependencies:

App build.gradle

app build.gradle.kts

dependencies {
    val airshipVersion = "androidSdkVersion"

    implementation("com.urbanairship.android:urbanairship-live-update:$airshipVersion")
}
app build.gradle

dependencies {
    def airshipVersion = "androidSdkVersion"

    implementation "com.urbanairship.android:urbanairship-live-update:$airshipVersion"
}

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:

Creating a handler

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"
    }
}
public class SampleLiveUpdateHandler extends CallbackLiveUpdateNotificationHandler() {
    @Override
    public void onUpdate(
        Context context,
        LiveUpdateEvent event,
        LiveUpdate update,
        LiveUpdateResultCallback<NotificationCompat.Builder> callback
    ) {
        // Read content_state fields from the Live Update payload
        int teamOneScore = update.getContent().optInt("team_one_score", 0);
        int teamTwoScore = update.getContent().optInt("team_two_score", 0);
        String statusUpdate = update.getContent().optString("status_update");

        // Expanded notification layout
        RemoteViews bigLayout = new RemoteViews(context.getPackageName(), R.layout.sports_big);
        bigLayout.setTextViewText(R.id.teamOneScore, String.valueOf(teamOneScore));
        bigLayout.setTextViewText(R.id.teamTwoScore, String.valueOf(teamTwoScore));
        bigLayout.setTextViewText(R.id.statusUpdate, statusUpdate);

        // Collapsed notification layout
        RemoteViews smallLayout = new RemoteViews(context.getPackageName(), R.layout.sports_small);
        smallLayout.setTextViewText(R.id.teamOneScore, String.valueOf(teamOneScore));
        smallLayout.setTextViewText(R.id.teamTwoScore, String.valueOf(teamTwoScore));

        // Create the notification builder
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setCategory(NotificationCompat.CATEGORY_EVENT)
            .setStyle(new 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.
        callback.onResult(LiveUpdateResult.ok(builder));
    }

    private static final String 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.

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

Registering a handler

class SampleAutopilot : Autopilot() {

    override fun onAirshipReady(context: Context) {

        Airship.liveUpdateManager.register(
            type = "notification",
            handler = SampleLiveUpdateHandler()
        )
    }
}
public class SampleAutopilot extends Autopilot() {

    @Override
    public void onAirshipReady(Context context) {
        LiveUpdateManager.shared().register("notification", new 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

Live Updates can be started from within the app.

Starting a Live Update

Airship.liveUpdateManager.start(
    name = "sports-game-123",
    type = "notification",
    content = jsonMapOf(
        "team_one_score" to 0,
        "team_two_score" to 0,
        "status_update" to "Game started!"
    )
)
Map<String, Object> content = new HashMap<>();
content.put("team_one_score", 0);
content.put("team_two_score", 0);
content.put("status_update", "Game started!");

LiveUpdateManager.shared().start(
    "sports-game-123",
    "notification",
    content
);

Updating Live Updates

Live Updates can be updated from within the app.

Updating a Live Update

Airship.liveUpdateManager.update(
    name = "sports-game-123",
    content = jsonMapOf(
        "team_one_score" to 3,
        "team_two_score" to 0,
        "status_update" to "Game started!"
    )
)
Map<String, Object> content = new HashMap<>();
content.put("team_one_score", 3);
content.put("team_two_score", 0);
content.put("status_update", "Game started!");

LiveUpdateManager.shared().update(
    "sports-game-123",
    content
);

Ending Live Updates

You can end a Live Update from within the app.

Ending a Live Update

Airship.liveUpdateManager.stop(
    name = "sports-game-123",
    content = jsonMapOf(
        "team_one_score" to 9,
        "team_two_score" to 6,
        "status_update" to "Game over!"
    )
)
Map<String, Object> content = new HashMap<>();
content.put("team_one_score", 9);
content.put("team_two_score", 6);
content.put("status_update", "Game over!");

LiveUpdateManager.shared().stop(
    "sports-game-123",
    content
);

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.

Clearing all Live Updates

Airship.liveUpdateManager.clearAll()
LiveUpdateManager.shared().clearAll();