Add Push Notifications to Your Android Chat App Using Kotlin
This story was originally published with collaboration with CometChat Pro, and Alex Booker on their tutorials blog.
For any kind of chat apps, notifications are the driver behind whole chat concept. These notifications lets users know that a new message has been sent, and asks to respond to it. This seems like a micro-interaction from user experience perspective, but this tiny interactions makes the whole chat a real-time conversation. This allows users to do multitasking while talking with their friends just as people do in real in-person or call meetings.
These notifications are called as Push Notifications in the language of Android / iOS development. In this article, you are going to learn on how to add push notifications in your Android chat apps in Kotlin using the simplicity or rather the beauty of CometChat Pro SDK.
As you are going to integrate push notifications in the Android chat apps, you will need at least a simple chat app with basic functionality such as sending / receiving messages. This article will use the Chaty sample app from this article and integrate push notifications in it when user gets any new message from other users. If you want to develop the chat app first, you can follow that article first and come back to this later. Or you can directly start from the already built chat app to learn about Push Notifications using the push-notifications-start branch of Chaty app here.
Here’s the final demo of the application you’re going to build in this article.
You can see in the demo GIF that Captain America (left) & Cyclops (right) are sending messages to each other and those messages are being delivered through push notifications of Android devices. Rather than just simply explaining the basic send/receive push notification, this article is going to explain some practical use-cases implemented in modern chat apps. These cases are listed below:
User can receive notifications when app is in background or completely removed from the Recent Apps in Android device.
User can receive notifications while app is active and in foreground and either Contacts or Profile or any other chat conversation screen are open.
User don’t receive notifications when the chat between sending user and receiving user is already open.
User can mute or unmute any specific user’s notifications at anytime.
Chaty Code Overview
Before getting started, if you have followed the Chaty article, then here’s a basic overview of the code and parts where you will be working in. And if you have your own chat app with your custom code, then you can skip this section.
LoginActivity.kt
manages the user login functionality. Once the user is successfully logged in, it opens the Contacts screen.ContactsActivity.kt
represents the Contacts screen. It fetches all other users data from CometChat Pro and shows in the screen.ProfileActivity.kt
displays the profile screen with the current logged in user’s name, email, avatar etc. This also manages the logout functionality of the user.ChatActivity.kt
handles the chat session of the users. This is responsible for sending and receiving messages between the users.ChatyApp.kt
is the main Application class of the whole app and is responsible for initializing the CometChat SDK when the app launches.strings.xml
file contains the CometChat API key and App ID. You should update this with your own keys now before moving further with this article.
Beside these classes, there are some model files for data holding and some Kotlin extensions to make utility methods easier to use.
Setting up Firebase Project
There are many different APIs for sending push notifications in Android such OneSignal, Firebase Messaging Service (FCM) etc. This tutorial will show you on how to integrate Firebase Messaging Service (FCM) service in your chat apps in a very easy way.
To integrate Firebase Messaging, you will need to create a project in Firebase Console. Go to Firebase Console and create a new account if you don’t have one. Once inside Firebase console, add a new project by clicking on the + Add project
button. Set the name of the project to MyChatApp
or anything you like.
On the step 2, uncheck the Enable Google Analytics for this project
and click Create Project
.
Now, as your project becomes ready, you will be redirected the project overview page. Here. add add a new Android app by clicking on the android robot icon.
You will be presented with a registration form now. Add the Package Name of your app. For example, com.cometchat.mychatapp
. You can skip the App Name, Debug Signing Certificate SHA-1 here. Click on Register App button.
This will allow you to download the google-services.json
file. This file is the configuration to integrate your app and Firebase and use any Firebase services from your app. In your case, that service is going to Firebase Messaging Service. Click on the Download google-services.json button and save it in the app
directory of your app code.
Now, in your project’s root build.gradle
file, copy this line (#12) in the dependencies
section like this:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.21'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.google.gms:google-services:4.3.2"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
And in your app’s build.gradle
file, add Google Services plugin (line #24), Firebase Messaging dependency (line #23), CometChat Push Notification extension (line #20 & #21) and change the applicationId
to the package name you added in the Firebase Console when creating the project (line #8). In this case, it was com.comet.mychatapp
like the code below.
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.comet.mychatapp"
minSdkVersion 21
// ...
}
// ...
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// ...
// CometChat Push Notification Extension
implementation "com.cometchat:pro-android-chat-sdk:1.8.+"
implementation "com.cometchat:pro-ext-pn:1.0.+"
// Firebase Messaging
implementation "com.google.firebase:firebase-messaging:20.0.0"
// ...
}
apply plugin: 'com.google.gms.google-services'
Press Sync Now button and this will be a successful build. Firebase is integrated in our app. Back to the Firebase Console, click the Next button and then continue to the console.
Now, click on the small gear icon beside the Overview
button, and select Project Settings
button.
Select the Cloud Messaging
tab and copy the Server key
to some temporary place. This will come handy in a moment.
Now, you are all set from the Firebase Console side. Let’s move to the CometChat dashboard now.
Enabling CometChat Push Notification Extension
One of the great features of CometChat Pro is to provide you the Extensions. These extensions let you add more functionality with the full control over it to enable/disable at anytime. So to integrate Firebase Messaging (FCM) push notifications feature into CometChat, you need to enable the Push Notification Extension from CometChat dashboard. Head to your CometChat dashboard and click on the Extensions
menu. Add Push Notification
from the extensions list.
Once the extension is enabled, paste the copied FCM Server key it into the extension by clicking on Actions → Settings.
Now, CometChat and Firebase are integrated with each other. This means that CometChat will be able to use FCM as the push notifications service. Let’s head into the coding now.
Add Firebase Messaging in Your App
As you have configured Firebase and CometChat project and setup their integration, its time to actually utilize this integration to send/receive push notifications in your chat apps. First step is to create the FirebaseMessagingService
class. This class will manage the functionality when any push notification is triggered for your apps. This is where you will actually write code to create mobile notification with vibration, sound, etc. to show it to the user.
Create a new class and name it FcmListenerService
. Now extend this from the FirebaseMessagingService
class, and override the following methods:
onNewToken()
- This is called when the device token gets generated. The device token change when app deletes the Instance ID, or app is restored on a new device, or the user uninstalls / re-installs the app, or the user deletes the app cache. This method will provide you the new device token. This token is used to send the push notifications to that specific device.onMessageReceived()
- This is the actual method which is called when any push notification is received by the device. This is where you will perform your notification functionality like generating the actual device notification with the data like sender name, message, avatar image etc. and show it to the user.
class FcmListenerService : FirebaseMessagingService()
{
override fun onNewToken(token: String) {
super.onNewToken(token)
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
Log.w("onMessageReceived", "Message Received from " + remoteMessage.from)
PNExtension.getMessageFromJson(JSONObject(remoteMessage.data["message"]), object : CometChat.CallbackListener<BaseMessage>(){
override fun onSuccess(baseMessage: BaseMessage?) {
when(baseMessage)
{
is TextMessage -> {
// Convert BaseMessage to TextMessage
var textMessage = baseMessage
// Send notification for this text message here
}
}
}
override fun onError(exception: CometChatException?) {
exception?.printStackTrace()
}
})
}
Once your class is ready, add it in the AndroidManifest.xml
file in the <application>
tag.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wajahatkarim3.chaty">
<application>
<!-- Firebase Messaging -->
<service
android:name=".FcmListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
In the traditional way, you have to get the device token in onNewToken()
method, and then send it to the server or any remotely available storage. And each time any user sends the message, you have to hit some remote service which dispatches notifications to the target devices. And then in the onMessageReceived()
method, you have to decode that message and extract data from it and show it to the user in form of any device notification.
But, CometChat is doing everything on its own and removes the dependency of the remote connection. It allows you to decode Firebase’s RemoteMessage
through the PNExtension.getMessageFromJson()
method in a very simple code.
Now we have all the boilerplate work in place. Let’s actually send / receive some messages.
Sending and receiving messages
Firebase offers two ways to send messages to other devices.
- Sending messages to the device IDs (tokens)
- Sending messages to the topics.
Since the device IDs / tokens are managed by CometChat and the app must not store any device ID (either own or some other user’s) due to security reasons, this article will use the topics approach to send messages. The topics in the FCM allows you to send messages to all devices that subscribed to a particular topic. The basic idea is that each user subscribes to any topic when logged in, and unsubscribes from it when logging out the app. Other users will send messages to that topic, and all the users who are subscribed to it will receive the messages.
To simply identify the topic of any user, you need to create the topic IDs with any pattern. This article will follow CometChat App ID + USER + USER ID
pattern. For example, a topic for the App ID with 12345 for the user 7689 will be 12345_user_7689
.
So, head to the LoginActivity.kt
file, and open the performLogin()
method. In the onSuccess()
callback of the CometChat.login()
method, user will subscribe to the the topic. You can subscribe to any topic with the FirebaseMessaging.getInstance().subsrcibeToTopic()
method.
// Register for the user type notifications
val topicId = getString(R.string.comet_app_id) + "_" + CometChatConstants.RECEIVER_TYPE_USER + "_" + user.uid
FirebaseMessaging.getInstance().subscribeToTopic(topicId)
To unsubscribe from this topic at the logging out, go to the ProfileActivity.kt
file. In the the onSuccess()
callback of the CometChat.logout()
, add this code below.
// Unsubscribe from the user notifications
val topicId = getString(R.string.comet_app_id) + "_" + CometChatConstants.RECEIVER_TYPE_USER + "_" + user.uid
FirebaseMessaging.getInstance().unsubscribeFromTopic(topicId)
And voila. You’re all set. Now, when you send message to any other user, the onMessageReceived()
method will be called in receiving user’s app. Since we have only added the line in logs, you will see a message in Logcat panel in Android Studio.
2019-09-30 06:44:55.457 5100-5271/com.comet.mychatapp W/onMessageReceived: Message Received from /topics/1234c23c123455_user_superhero1
You can do anything as per your app requirements regarding notifications in this onMessageReceived
method. For the actual device notification, we will use a third-party library called as Notify available at Github to generate the notifications easily. Follow the instructions on the library’s Read Me page and add this code in the onMessageReceived()
in the when
block for TextMessage
to show the notification on device.
when(baseMessage)
{
is TextMessage -> {
// Convert BaseMessage to TextMessage
var textMessage = baseMessage
// Send notification for this text message here
val notificationId = 124952
Notify
.with(this@FcmListenerService)
.content { // this: Payload.Content.Default
title = textMessage.sender.name
text = textMessage.text
}
.alerting("high_priority_notification") {
channelImportance = Notify.IMPORTANCE_HIGH
}
.show(notificationId)
}
}
Now, when you send messages to any user, you will see a device notification like this.
Mute / Unmute Any User’s Notifications
One important feature of any modern chat app is the ability to mute / unmute notifications from other specific users. CometChat or Firebase do not directly provide any method to do this, but you can do it very easily with using some basic logic in the app.
In the Chaty app, we have added a popup menu in the Contacts screen where user can mute / unmute others users. When someone mutes any other user, the app stores the user’s ID with prefix mute-
, for e.g. mute-123456
in the SharedPreferences
. This lets the app know that user 123456
is muted by the current logged in user. So, now in the onMessageReceived()
method, app checks if the sender’s ID is in the muted list, then it doesn’t generate notification at all like the code below.
when(baseMessage)
{
is TextMessage -> {
// Convert BaseMessage to TextMessage
var textMessage = baseMessage
var prefs = PreferenceManager.getDefaultSharedPreferences(this@FcmListenerService)
if ( prefs.getBoolean("mute-${textMessage.sender.uid}", false) )
{
// Don't send the notification as the contact is muted for notifications
}
else
{
// Send notification for this text message here
val notificationId = 124952
Notify
.with(this@FcmListenerService)
.content { // this: Payload.Content.Default
title = textMessage.sender.name
text = textMessage.text
}
.alerting("high_priority_notification") {
channelImportance = Notify.IMPORTANCE_HIGH
}
.show(notificationId)
}
}
}
Now, when you add mute-USER_ID
in the app’s SharedPreferences
, then app won’t show notifications of that specific user. You can see in the ContactsActivity
in the final code at push-notifications-finish branch of Chaty on Github how this small tactic is being used to mute / unmute users.
Showing Notifications in Specific Screens
Now as mute functionality is working, there’s one other case where you can avoid to show notifications. For example, if the user is already chatting with other user, and that specific chat is already open, then it doesn’t make sense to send / receive notifications while in that screen. The Chaty app handles this in a very similar approach as mute / unmute functionality is doing. The app is storing isChatOn
and that user’s ID in the SharedPreferences
in the chatUserId
. And then in the onMessageReceived()
method in FcmListenerService
class, we are adding another check to see if the app should generate notification or not.
when(baseMessage)
{
is TextMessage -> {
// Convert BaseMessage to TextMessage
var textMessage = baseMessage
var prefs = PreferenceManager.getDefaultSharedPreferences(this@FcmListenerService)
if ( prefs.getBoolean("mute-${textMessage.sender.uid}", false)
|| (prefs.getBoolean("isChatOn", false) && prefs.getString("chatUserId", "").equals(textMessage.sender.uid)) )
{
// Don't send the notification as the contact is muted for notifications
}
else
{
// Send notification for this text message here
val notificationId = 124952
Notify
.with(this@FcmListenerService)
.content { // this: Payload.Content.Default
title = textMessage.sender.name
text = textMessage.text
}
.alerting("high_priority_notification") {
channelImportance = Notify.IMPORTANCE_HIGH
}
.show(notificationId)
}
}
}
This tells the app that chat screen is already open of the sender user, so there’s no need to generate the device notification.
You can do a lot of other stuff like generate popup notifications, showing unread messages count bubble etc. with this simple tactic. You can see how ChatActivity.kt
is handling the isChatOn
and chatUserId
values in SharedPreferences
to allow this simple feature on the push-notifications-finish branch of Chaty app on Github.
Conclusion
You have now learned how to add push notifications feature to your Andorid apps in Kotlin using CometChat Pro and Firebase Messaging Service. You also learned about how to send / receive message notifications, mute / unmute specific users, and handling notifications for specific screens. You also learned about CometChat and how easy it is to build a custom chat app using its chat SDK. Congratulations on making it this far!
Don’t forget to checkout CometChat documentation for advanced features such as sending image and voice chat etc. And for the whole code of this tutorial, check push-notification-finish
branch of the Chaty app on Github here.