Push Notifications for Android and WebCopied!

If you're looking to speed up your Android app development, you can use our Android Chat SDK to get started. It's feature rich and will speed up your development!

Using Firebase, your users apps can receive push notifications directly to their client app for new messages when offline. In order to push notifications to Android devices, you need to have an application on Firebase and configure your Stream account using the Firebase server key.

Retrieving the Server Key from FirebaseCopied!

Step 1Copied!

From the Firebase Console, select the project your app belongs to

Step 2Copied!

Click on the gear icon next to Project Overview and navigate to Project settings

Step 3Copied!

Navigate to the Cloud Messaging tab

Step 4Copied!

Under Project Credentials, locate the Server key and copy it

Step 5Copied!

Upload the Server Key in your chat dashboard

Step 6Copied!

Save your push notification settings changes

OR - Copied!

Upload the Server Key via API call

1
2
3
4
5
6
7
await client.updateAppSettings({ 
    firebase_config: { 
        server_key: 'server_key', 
        notification_template: `{"message":{"notification":{"title":"New messages","body":"You have {{ unread_count }} new message(s) from {{ sender.name }}"},"android":{"ttl":"86400s","notification":{"click_action":"OPEN_ACTIVITY_1"}}}}`, 
        data_template: `{"sender":"{{ sender.id }}","channel":{"type": "{{ channel.type }}","id":"{{ channel.id }}"},"message":"{{ message.id }}"}` 
    }, 
});
Remeber to add google-services.json file to your project source directory. You can download it in the Firebase console. For more information take a look at Firebase setup tutorial https://firebase.google.com/docs/android/setup.

Registering a device at Stream BackendCopied!

Once you configure Firebase server key and set it up on Stream dashboard a device which is supposed to receive push notifications needs to be registered at Stream backend. This is usually done by requesting a Firebase device token and passing it to the backend as follows:

1
2
3
4
5
6
7
client.addDevice("firebase-token").enqueue { result -> 
    if (result.isSuccess) { 
        // Device was successfully registered 
    } else { 
        // Handle result.error() 
    } 
}
1
2
3
4
5
6
7
client.addDevice("firebase-token").enqueue(result -> { 
    if (result.isSuccess()) { 
        // Device was successfully registered 
    } else { 
        // Handle result.error() 
    } 
});
Device registration logic above is also performed automatically when you set up User data in the Chat or ChatClient objects. If you use Android Chat SDK or Android ChatClient low level SDK there is no need to register the device manually.

Setting up notification data payload at Stream DashboardCopied!

1. Setup the following push notification data payload at Stream Dashboard:

1
2
3
4
5
{ 
  "message_id": "{{ message.id }}", 
  "channel_id": "{{ channel.id }}", 
  "channel_type": "{{ channel.type }}" 
}

2. If you are using Stream Chat Android SDK add the following notifications config into your Chat building logic:

1
2
3
4
5
6
7
8
9
val notificationsConfig = NotificationConfig( 
    firebaseMessageIdKey = "message_id", 
    firebaseChannelIdKey = "channel_id", 
    firebaseChannelTypeKey = "channel_type", 
) 
 
ChatClient.Builder("YOUR_API_KEY", context) 
    .notifications(ChatNotificationHandler(context, notificationsConfig)) 
    .build()
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
int notificationChannelId = R.string.stream_chat_notification_channel_id; 
int notificationChannelName = R.string.stream_chat_notification_channel_name; 
int smallIcon = R.drawable.stream_ic_notification; 
String firebaseMessageIdKey = "message_id"; 
String firebaseMessageTextKey = "message_text"; 
String firebaseChannelIdKey = "channel_id"; 
String firebaseChannelTypeKey = "channel_type"; 
String firebaseChannelNameKey = "channel_name"; 
int errorCaseNotificationTitle = R.string.stream_chat_notification_title; 
int errorCaseNotificationContent = R.string.stream_chat_notification_content; 
boolean useProvidedFirebaseInstance = true; 
 
NotificationConfig notificationsConfig = new NotificationConfig( 
        notificationChannelId, 
        notificationChannelName, 
        smallIcon, 
        firebaseMessageIdKey, 
        firebaseMessageTextKey, 
        firebaseChannelIdKey, 
        firebaseChannelTypeKey, 
        firebaseChannelNameKey, 
        errorCaseNotificationTitle, 
        errorCaseNotificationContent, 
        useProvidedFirebaseInstance 
); 
 
new ChatClient.Builder("YOUR_API_KEY", context) 
        .notifications(new ChatNotificationHandler(context, notificationsConfig)) 
        .build();
You can create your custom instance of ChatNotificationHandler class by inheriting from it and overriding its methods. This is the way to customize notification appearance, title, icon, etc.

3. Add FirebaseService to the AndroidManifest.xml, inside the <application> element:

1
2
3
4
5
6
7
<service 
    android:name="io.getstream.chat.android.livedata.service.sync.OfflineSyncFirebaseMessagingService" 
    android:exported="false"> 
    <intent-filter> 
        <action android:name="com.google.firebase.MESSAGING_EVENT" /> 
    </intent-filter> 
</service>

If you are using low-level SDK (Stream Chat Android Client) then declare a different class in the manifest instead:

1
2
3
4
5
6
7
<service 
    android:name="io.getstream.chat.android.livedata.service.sync.OfflineSyncFirebaseMessagingService" 
    android:exported="false"> 
    <intent-filter> 
       <action android:name="com.google.firebase.MESSAGING_EVENT" /> 
    </intent-filter> 
 </service>

Handling notifications from multiple backend servicesCopied!

In case you want to use multiple push notifications providers, you may not want to use the OfflineSyncFirebaseMessagingService class. If you prefer to have your own implementation of FirebaseMessagingService and still work with Stream push notifications you will need to use the PushMessageSyncHandler class and invoke its methods inside your onNewMessage() and onNewToken() functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import com.google.firebase.messaging.FirebaseMessagingService 
import com.google.firebase.messaging.RemoteMessage 
import io.getstream.chat.android.livedata.service.sync.PushMessageSyncHandler 
 
class CustomFirebaseMessagingService: FirebaseMessagingService() { 
    private val pushDataSyncHandler: PushMessageSyncHandler = 
        PushMessageSyncHandler(this) 
 
    override fun onNewToken(token: String) { 
        // update device's token on Stream backend 
        pushDataSyncHandler.onNewToken(token) 
    } 
 
    override fun onMessageReceived(message: RemoteMessage) { 
        if (pushDataSyncHandler.isStreamMessage(message)) { 
            // handle RemoteMessage sent from Stream backend 
            pushDataSyncHandler.onMessageReceived(message) 
        } else { 
            // handle RemoteMessage from other source 
        } 
        stopSelf() 
    } 
}

Redirection from notification to appCopied!

In order to adjust redirection from notification to the specific Activity in your app (when clicking on the notification), you need to an Intent which will hold a target Activity and metadata of the message. In order to define this Intent you should override ChatNotificationHandler class and pass it to ChatClient.Builder.

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
val notificationHandler = MyNotificationHandler(context, notificationsConfig) 
 
    ChatClient.Builder("{{ api_key }}", context) 
                .notifications(notificationHandler) 
                .build() 
                 
    class MyNotificationHandler(context: Context, notificationConfig: NotificationConfig) : 
        ChatNotificationHandler(context, notificationConfig) { 
 
        override fun getNewMessageIntent( 
            messageId: String, 
            channelType: String, 
            channelId: String 
        ): Intent = Intent(context, MainActivity::class.java).apply { 
            putExtra(EXTRA_CHANNEL_ID, channelId) 
            putExtra(EXTRA_CHANNEL_TYPE, channelType) 
            putExtra(EXTRA_MESSAGE_ID, messageId) 
        } 
 
        companion object { 
            const val EXTRA_CHANNEL_ID = "extra_channel_id" 
            const val EXTRA_CHANNEL_TYPE = "extra_channel_type" 
            const val EXTRA_MESSAGE_ID = "extra_message_id" 
        } 
    }

Possible issuesCopied!

if RemoteMessage does not arrive (is not intercepted) check if notification template is defined correctly at the Stream Dashboard. Read more about difference between notification and data here.