DEV Community

Wajahat Karim šŸ‡µšŸ‡°
Wajahat Karim šŸ‡µšŸ‡°

Posted on • Originally published at wajahatkarim.com on

Add Push Notifications to Your Android Chat App Using Kotlin

Originally published at wajahatkarim.com.

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.

Chaty Push Notifications - DemoChaty Push Notifications - Demo

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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()
          }
      })
  }
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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.

  1. Sending messages to the device IDs (tokens)
  2. 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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)

    }
}
Enter fullscreen mode Exit fullscreen mode

Now, when you send messages to any user, you will see a device notification like this.

Notification when app is in backgroundNotification when app is in background


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)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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.


If you liked this article, you can read my new articles below:

āš” Is Your Android Studio Always Slow? šŸš€Hereā€™s How to Speed Up Immediately.

šŸ“† Reflections on 2018 ā€” My Year In Review


Wajahat Karim is a graduate from NUST, Islamabad, an experienced mobile developer, an active open source contributor, and co-author of two books Learning Android Intents and Mastering Android Game Development with Unity. In his spare time, he likes to spend time with his family, do experiments on coding, loves to write about lots of things (mostly on blog and medium) and is passionate contributor to open source. In June 2018, one of his library became #1 on Github Trending. His libraries have about 2000 stars on Github and are being used in various apps by the developers all around the globe. Follow him on Twitter and Medium to get more updates about his work in Writing, Android and Open Source.

Also, if you have any questions youā€™d like him to answer, contact him through his website at wajahatkarim.com with DEAR WAJAHAT in the subject line.

Top comments (0)