Live Activities for Android
Learn how to emulate Live Updates in the Android Braze SDK. Although Live Updates aren’t officially supported, this guide will show you how to emulate their behavior, so you can display interactive lock-screen notifications similar to Live Activities for the Swift Braze SDK. Unlike official Live Updates, this functionality can be implemented for older Android versions.
How it works
You can use the IBrazeNotificationFactory
interface to customize how Braze push notifications are displayed. By extending BrazeNotificationFactory
, Braze will call your factory’s createNotification()
method before the notification is displayed to the user. It will then pass a payload containing custom key-value pairs sent through the Braze dashboard or REST API.
Emulating a Live Update
Live Updates aren’t natively supported in the Android OS. The following section only shows you how to emulate their general behavior.
In this section, you’ll partner with Superb Owl, the host of a new game show where wildlife rescue teams compete to see who can save the most owls. They’re looking to leverage Live Updates in their Android app, so they can display the status of an on-going match and make dynamic updates to the notification in realtime.
Prerequisites
Before you can use this feature, you’ll need to integrate the Android Braze SDK.
Step 1: Add a custom layout
You can add one or more custom Live Update layouts to your project. These are helpful for handling how notifications are displayed when collapsed or expanded. Your directory structure should be similar to the following:
1
2
3
4
5
6
.
├── app/
└── res/
└── layout/
├── liveupdate_collapsed.xml
└── liveupdate_expanded.xml
In each XML file, create a custom layout. Superb Owl created the following layouts for their collapsed and expanded Live Updates:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/notification_title"
style="@style/TextAppearance.Compat.Notification.Title"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
Step 2: Create a custom notification factory
In your application, create a new file named MyCustomNotificationFactory.kt
that extends BrazeNotificationFactory
to handle how Braze Live Updates are displayed.
In the following example, Superb Owl created a custom notification factory to display a Live Update for on-going matches. In the next step, they’ll create a new method called getTeamInfo
to map a team’s data to the activity.
Show the sample code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import android.app.Notification
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import com.braze.models.push.BrazeNotificationPayload
import com.braze.push.BrazeNotificationFactory
import com.braze.push.BrazeNotificationUtils.getOrCreateNotificationChannelId
import com.braze.support.BrazeLogger.brazelog
class MyCustomNotificationFactory : BrazeNotificationFactory() {
override fun createNotification(payload: BrazeNotificationPayload): Notification? {
if (payload.extras.containsKey("live_update")) {
val kvp = payload.extras
val notificationChannelId = getOrCreateNotificationChannelId(payload)
val context = payload.context
if (context == null) {
brazelog { "BrazeNotificationPayload has null context. Not creating notification" }
return null
}
val team1 = kvp["team1"]
val team2 = kvp["team2"]
val score1 = kvp["score1"]
val score2 = kvp["score2"]
val time = kvp["time"]
val quarter = kvp["quarter"]
// Superb Owl will define the 'getTeamInfo' method in the next step.
val (team1name, team1icon) = getTeamInfo(team1)
val (team2name, team2icon) = getTeamInfo(team2)
// Get the layouts to use in the custom notification.
val notificationLayoutCollapsed = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.liveupdate_collapsed)
val notificationLayoutExpanded = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.liveupdate_expanded)
// Very simple notification for the small layout
notificationLayoutCollapsed.setTextViewText(
R.id.notification_title,
"$team1 $score1 - $score2 $team2\n$time $quarter"
)
notificationLayoutExpanded.setTextViewText(R.id.score, "$score1 - $score2")
notificationLayoutExpanded.setTextViewText(R.id.team1name, team1name)
notificationLayoutExpanded.setTextViewText(R.id.team2name, team2name)
notificationLayoutExpanded.setTextViewText(R.id.timeInfo, "$time - $quarter")
notificationLayoutExpanded.setImageViewResource(R.id.team1logo, team1icon)
notificationLayoutExpanded.setImageViewResource(R.id.team2logo, team2icon)
val customNotification = NotificationCompat.Builder(context, notificationChannelId)
.setSmallIcon(R.drawable.notification_small_icon)
.setStyle(NotificationCompat.DecoratedCustomViewStyle())
.setCustomContentView(notificationLayout)
.setCustomBigContentView(notificationLayoutExpanded)
.build()
return customNotification
} else {
// Use the BrazeNotificationFactory for all other notifications
return super.createNotification(payload)
}
}
}
Step 3: Map custom data
In MyCustomNotificationFactory.kt
, create a new method for handling data when Live Updates are displayed.
Superb Owl created the following method to map each team’s name and logo to expanded Live Updates:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CustomNotificationFactory : BrazeNotificationFactory() {
override fun createNotification(payload: BrazeNotificationPayload): Notification? {
// Your existing code
return super.createNotification(payload)
}
// Your new method
private fun getTeamInfo(team: String?): Pair<String, Int> {
return when (team) {
"WBF" -> Pair("Wild Bird Fund", R.drawable.team_wbf)
"OWL" -> Pair("Owl Rehab", R.drawable.team_owl)
else -> Pair("Unknown", R.drawable.notification_small_icon)
}
}
}
Step 4: Set the custom notification factory
In your application class, use customBrazeNotificationFactory
to set your custom notification factory.
1
2
3
4
5
6
7
8
9
10
import com.braze.Braze
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Tell Braze to use your custom factory for notifications
Braze.customBrazeNotificationFactory = MyCustomNotificationFactory()
}
}
Step 5: Send the activity
You can use the /messages/send
REST API endpoint to send a push notification to a user’s Android device.
Example curl command
Superb Owl sent their request using the following curl command:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
curl -X POST "https://BRAZE_REST_ENDPOINT/messages/send" \
-H "Authorization: Bearer {REST_API_KEY}" \
-H "Content-Type: application/json" \
--data '{
"external_user_ids": ["USER_ID"],
"messages": {
"android_push": {
"title": "WBF vs OWL",
"alert": "2 to 4 1:33 Q4",
"extra": {
"live_update": "true",
"team1": "WBF",
"team2": "OWL",
"score1": "2",
"score2": "4",
"time": "1:33",
"quarter": "Q4"
},
"notification_id": "ASSIGNED_NOTIFICATION_ID"
}
}
}'
While curl commands are helpful for testing, we recommend handling this call in your backend where you’re already handling your iOS Live Activities.
Request parameters
Key | Description |
---|---|
REST_API_KEY |
A Braze REST API key with messages.send permissions. This can be created in the Braze dashboard from Settings > API Keys. |
BRAZE_REST_ENDPOINT |
Your REST endpoint URL. Your endpoint will depend on the Braze URL for your instance. |
USER_ID |
The ID of the user you are sending the notification to. |
messages.android_push.title |
The message’s title. By default, this is not used for the custom notification factory’s live notifications, but it may be used as a fallback. |
messages.android_push.alert |
The message’s body. By default, this is not used for the custom notification factory’s live notifications, but it may be used as a fallback. |
messages.extra |
Key-value pairs that the custom notification factory uses for live notifications. You can assign any string to this value—however, in the example above, live_updates is used to determine if it’s a default or live push notification. |
ASSIGNED_NOTIFICATION_ID |
The notification ID you want to assign to the chosen user’s live notification. The ID must be unique to this game, and must be used in order to update their existing notification later. |
Step 6: Update the activity
To update the existing Live Update with new data, modify the relevant key-value pairs assigned to messages.extra
, then use the same notification_id
and call the /messages/send
endpoint again.