Tutorial: Android Kotlin Mobile Push Notifications and PubNub

Learn how to leverage PubNub with Android Kotlin mobile push notifications in a chat application.

Step 01

Android Kotlin Environment Setup

This tutorial will guide you through how to add push notifications to a chat application using PubNub. The tutorial will focus on how to integrate mobile push notifications in a chat application that is already running on the PubNub network. If you would like to learn more about how the chat application was constructed, follow our Android Kotlin Simple Chat Application Tutorial.

Download Source Application

Download the JetNews chat app from the GitHub Repository. You will be selecting the push-notifications branch which contains the completed chat application with mobile push notifications.

In a terminal, navigate to an empty folder of your choosing. Enter the following command to clone the Repository.

git clone https://github.com/PubNubDevelopers/android-kotlin-chat-tutorial.git

Checkout the push-notifications branch.

git checkout push-notifications

Watch the video or follow the steps below to set up your environment.

Download Android Studio

Download the latest, stable version of Android Studio for your operating system if you don't already have it. This download includes the IDE, Android SDK, and Android Emulator.

Create a Firebase Project

You will be using Google's Firebase Cloud Messaging (FCM) as the Service Provider to deliver mobile push notifications to the application. You will set up and configure a Firebase project that will be directly associated with your application.

  1. Navigate to the Firebase console.

  2. Press the Create a project button and enter in the name of your project. The Firebase console will automatically replace hyphens with spaces. Press continue.

  3. Disable Google Analytics for your application by clicking on the blue slider as you will not need it for this project. Press create project.

  4. After some time, the project will be ready. Press continue. You will be redirected to the Project Overview page.

  5. In the center of the page, click on the Android App Icon in the "Get started by adding Firebase to your app" section to add an Android application to your Firebase project.

  6. Register the Android app by providing the Android package name (in this case com.example.jetnews). You can find this package name by navigating to the app level build.gradle file (usually app/build.gradle), and looking at android -> defaultConfig -> applicationId. Click on Register App.

  7. Download the google-services.json config file. This file is the bridge between your FCM project and your Android application. You will need to add this file to the application module root directory (app). This will be detailed shortly. Click next.

  8. Do not add the Firebase SDK. You will be using the PubNub SDK and a different Firebase library. These dependencies have already been added to the application and will be discussed shortly.

  9. Click next, and then click Continue to console.

Obtain Server Key Token

You will need to now obtain the server key token necessary to have PubNub send push payloads to FCM to deliver mobile push notifications to devices. The steps are summarized below but please also see our documentation for Android Mobile Push Notifications for more information.

  1. In the Firebase console, select the project previously created if you are not already in the Project Overview page for that project.

  2. In the top left corner, click on the gear icon next to the Project Overview label and select Project settings. The Project settings page contains information about your application, and allows you to edit, configure, and obtain necessary information for your application. You can also re-download the google-services.json file in case you have lost it previously.

  3. Click on the Service accounts tab at the top of the page.

  4. Click 'Generate new private key'. It does not matter which Admin SDK configuration snippet you have selected.

  5. Retain the downloaded key file - you will need to upload this to the PubNub admin portal in a future step.

Switch to Project View

Open the application in Android Studio. Switch to the Project View instead of Android View in the top left corner, as this tutorial describes locations of files in this Project View.

The tutorial will be referencing this view frequently when adding files, and will refer to this window as the project file viewer. Please keep this side toolbar open when developing your project.

IDE interface with project file structure including folders for production and tests.

Add the google-services.json File

Move the google-services.json file that was downloaded earlier to the app-level module folder.

Project directory structure in an Integrated Development Environment showing various configuration files such as build.gradle and google-services.json.

Create Android Virtual Device

Create an android virtual device via the Device Manager to emulate the application in a virtual environment.

Project Level Dependencies

Open the project level build.gradle file. The Google Services plugin dependency is required to be able to have the app level build.gradle files apply the plugin to open the google-services.json file.

... dependencies { ... classpath 'com.google.gms:google-services:4.3.10' }

App Level Dependencies

Open the app-level build.gradle file (app/build.gradle). The following dependencies are necessary to add mobile push notifications to an application using PubNub:

  1. In the plugins block, the google-services plugin is used to load the google-services file to be able to communicate with FCM.

  2. In the dependencies block, the Kotlin SDK and Chat components for Android dependencies are used for the PubNub API libraries for communicating with the PubNub network.

  3. In the dependencies block, the Firebase Messaging dependency is used for the Legacy Cloud Messaging API functionality.

plugins { ... id 'com.google.gms.google-services' //Google Services plugin to load the google-services.json file. } ... //PubNub Dependencies implementation "com.pubnub:pubnub-kotlin:7.0.0" implementation "com.pubnub.components:chat-android:0.2.0" //FCM Dependencies implementation 'com.google.firebase:firebase-messaging-ktx:23.0.5'

Service that Extends FirebaseMessagingService

To receive messages from FCM in your application, a service that extends FirebaseMessagingService needs to be added to your Android application. This service is represented by the MyFirebaseMessagingService.kt class in app/src/main/java/com.example.jetnews/utils.

This class extends the onNewToken and onMessageReceived callbacks. onNewToken is called whenever FCM assigns the Android device a new or different token (when starting the application for the first time or when the old token expires). You would then register the device token on a channel in the PubNub network. onMessageReceived displays mobile push notifications once received from FCM based on the type of push message received. The functionality of the class will be detailed in the Build and run section of the tutorial.

class MyFirebaseMessagingService : FirebaseMessagingService() { ... override fun onNewToken(token: String) { //Called whenever a new device runs the Android application. Registers in FCM and in PubNub. } //Handle when the device has received a mobile push notification from FCM. override fun onMessageReceived(remoteMessage: RemoteMessage) { ... }

This class has been added as a service to the app's AndroidManifest.xml to listen for and handle any FCM operations. The metadata for default_notification_channel_id, default_notification_channel_name, and default_notification_channel_description are also located below the service. A Notification ID is required to be set when creating a notification channel, and FCM will use this value whenever incoming messages do not explicitly set a notification channel. You can learn more about this metadata in Android's documentation. You'll learn more about creating notifications in the build and run section of this tutorial.

Internet Permissions

View the top of the application's AndroidManifest.xml file. The PubNub Network API requires internet permissions, otherwise you cannot connect to the PubNub Network. View the internet permissions in the manifest element, before the application block.

Step 02

Create keys

My own keys

You'll need to create an account to use your own keys

  1. Sign in to your PubNub Dashboard.

  2. Go to Keysets using the left hand side navigation.

  3. Click the “Create New Keyset” button on the upper right hand-side of the portal.

  4. A pop-up appears. Give the keyset a name and click the “Create” button.

  5. Click on the newly created keyset.

  6. Enable three features for the keyset to be able to connect to the PubNub Network: Presence, Message Persistence and Stream Controller.

  7. Enable Presence by clicking on the slider to turn it on. A pop-up will require that you enter in “ENABLE”. Enter in “ENABLE” in all caps and then press the “Enable” button. You can leave the default settings.

  8. Enable Message Persistence by clicking on the slider to turn it on. You can leave the default settings.

  9. Enable Stream Controller by clicking on the slider to turn it on if it is already not enabled. You can leave the default settings.

  10. Enable Mobile Push Notifications by clicking on the slider to turn it on. In the Firebase Cloud Messaging section, upload the private key file you generated previously from the Firebase console. For more help with this process, please refer to our Mobile Android Push Notifications documentation.

  11. Click on the “Save Changes” button in the bottom right corner of the portal. After a few seconds, a confirmation message will appear in the upper-left corner of the portal confirming that these changes have been saved. 

  12. Scroll back to the top of the portal. Copy the Publish key by clicking on the copy icon and paste it below

  13. Copy the Subscribe Key by clicking on the copy icon and paste it below

Step 03

Build and run

Watch the video or follow the steps below to build and run your application.

JavaScript
//This code box will update soon as you
//progress through the video
//Use the copy icon in the top right to quickly copy
//code as you're following along

Step 1. Add PubNub Keys

Open the gradle.properties file. Copy the publish and subscribe keys above and replace the value of the two constants at the end of this file: one for the PubNub Publish Key, and the other for the PubNub Subscribe Key.

kotlin
# PubNub Publish and Subscribe Keys needed to connect to PubNub's realtime communication network.
PUBNUB_PUBLISH_KEY="publish_key_here"
PUBNUB_SUBSCRIBE_KEY="subscribe_key_here"

Step 2. PubNub Companion Object

Navigate to the PubNubObj.kt class in app/src/main/java/com.example.jetnews/utils. This class is used to house a single instance of a PubNub object as a companion object to be shared across all activities.

The pubNub object is used to connect to the PubNub network. It is set by passing in a PNConfiguration containing a uuid, a publishKey, and a subscribeKey. The publish and subscribe keys are called from build.gradle by using the BuildConfig object, which is generated when building the application. The UUID is six randomly generated characters.

The public function getInstance is used to obtain this shared pubNub object instance across all activities.

kotlin
class PubNubObj {
    companion object PubNubObj {
        private val pubNub : PubNub = PubNub(
            PNConfiguration(uuid = UUID.randomUUID().toString().substring(0,5)).apply {
                publishKey = BuildConfig.PUBLISH_KEY
                subscribeKey = BuildConfig.SUBSCRIBE_KEY
            }
        )

    //Return the instance of the PubNub companion object to be used across all instances.
    fun getInstance() : PubNub {
        return pubNub
    }
}

Step 3. Application Entry Point

Navigate to MainActivity.kt class in app/src/main/java/com.example.jetnews, the starting point of the application. The pubNub companion object is obtained from the singleton class by calling getInstance. The onCreate function initializes the JetNews application. Included in this function are window flags to hide the system bar, as some APIs in the AVD have their navigation bars block access to the buttons in the JetNews app.

kotlin
   private val pubNub: PubNub = PubNubObj().getInstance() //Using a singleton PubNub object to persist across the activities.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //Setting to hide the system bar, as it interferes with some of the buttons in some
        //AVDs. Drag up from the bottom to bring up the navigation buttons, or use the navigation
        //buttons in the emulator toolbar.
        WindowCompat.setDecorFitsSystemWindows(window, false)
        this.window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_FULLSCREEN
                or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
        val appContainer = (application as JetnewsApplication).container
        setContent {
            val windowSizeClass = rememberWindowSizeClass()
            JetnewsApp(appContainer, windowSizeClass)
        }
  ...
}

onCreate also calls createNotificationChannel to create the notification channel necessary to display incoming mobile push notifications that the client app needs to handle (push message type of notification while in-app, or the push message type of data). You register the channel with the system, provide channel settings and define the channel ID. You will use the same channel ID when displaying the received notifications in MyFireBaseMessagingService.kt, discussed shortly.

kotlin
//Creates the notification channel necessary to display incoming mobile push notifications
//Register the channel with the system
private fun createNotificationChannel() {
    val notificationManager: NotificationManager =
        getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is new and not in the support library
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = getString(R.string.default_notification_channel_name)
        val descriptionText = getString(R.string.default_notification_channel_description)
        val importance = NotificationManager.IMPORTANCE_HIGH
        val channel = NotificationChannel( getString(R.string.default_notification_channel_id),name, importance).apply {
            description = descriptionText
        }
        //Channel settings
        channel.description = descriptionText
        channel.enableLights(true)
        channel.lightColor = Color.RED
        channel.enableVibration(true)
        channel.vibrationPattern =
            longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
        notificationManager?.createNotificationChannel(channel)
    } else {
        Log.d("TAG", "Build.VERSION.SDK_INT < 0. Update SDK.")
    }
}

Step 4. The Chat Room

The chat room of the application is created and managed using PubNub's Chat Components in ChatActivity.kt and Chat.kt, in which both are located in app/src/main/java/com.example.jetnews/chat. In this tutorial, clicking on the chat icon will lead to the same chat room for each article. The PubNub channel id is "default-article" for this chat room. The chat room itself is built with PubNub's Chat Components, which are easy-to-use, prebuilt UI and chat building blocks to reduce app development resources in getting an application to market.

If you would like to learn more about how chat built with PubNub was added to an open-source application not built by PubNub, follow how to build a simple chat app with Android Kotlin, which uses this same JetNews base application.

Step 5. Register Devices & Handle Incoming Mobile Push Notifications

Navigate to the MyFirebaseMessagingService class in app/src/main/java/com.example.jetnews/utils. As discussed in the setup environment step, this class is used as a service to register any new devices with the PubNub network when running the application for the first time and handle any new mobile push notifications received from FCM. This is possible by overriding the onNewToken and onMessageReceived functions.

The pubNub companion object will be used to register the device in specified channels on the PubNub Network. This will occur whenever a new device is running the application for the first time and will trigger the onNewToken function. This class extends the onNewToken and onMessageReceived callbacks.

The onNewToken function is called whenever FCM assigns the Android device a new or different token (when starting the application for the first time or when the old token expires). You would then call sendRegistrationToPubNub, which registers the device's token (received from FCM) as the device ID to a list of specified channels. Anytime a message is sent across the PubNub network, PubNub checks to see if there is a mobile push notification payload. If there is one, PubNub will check the device IDs on those associated channels, and if found, forwards the push to FCM to deliver the mobile push notification to those devices. You declare the push type of the notification as well during this registration. In this instance, it is only of type FCM.

kotlin
private val pubNub: PubNub = PubNubObj().getInstance()

//Called when the app is first opened from the device.
//Registers the device on the PubNub network to receive mobile push notfications.
override fun onNewToken(token: String) {
    sendRegistrationToPubNub(token)
}

//Register the device on PubNub Channels to enable push notifications on those channels.
private fun sendRegistrationToPubNub(token : String) {
    if (token != null) {
        pubNub.addPushNotificationsOnChannels(
            pushType = PNPushType.FCM,
            deviceId = token,
            channels = listOf("default,default-article,ch1,ch2") //provide a list of channels to enable push on them.
        ).async { result, status ->
            Log.d("TAG","Status: $status, Result: $result")
        }
    }
}

The onMessageReceived function is called whenever the device has received a mobile push notification from FCM. If you do not override this function, FCM will automatically handle the notification for you: FCM will automatically display the mobile push notification that you can click on, but will always bring you to the Main Activity of the application, regardless of the original purpose of the mobile push notification. Overriding the function allows you to check if the notification contains a message type of notification or data, and configure and display the notification yourself, giving you more control over your notifications and what happens when users click on them.

Keep in mind that even if you do override the onMessageReceived function and display the notifications yourself, if the message type is of type notification and the app is background or closed, FCM will automatically handle the notification. You can work around this by sending a message type of data instead, in which the client app will always handle this mobile push notification.

kotlin
//Handle when the device has received a mobile push notification from FCM.
//Handled whenever App is in Foreground, or receive Data payload.
override fun onMessageReceived(remoteMessage: RemoteMessage) {
    super.onMessageReceived(remoteMessage)
    var title : String = ""
    var body : String = ""

    // Check if message contains a notification payload.
    // Handled client-side if app in foreground.
    // Handled by FCM if app in background/closed.
    remoteMessage?.data?.let {
        //Log.d(TAG, "Message data payload: " + remoteMessage.data)
        title = remoteMessage.data?.get("title") .toString()
        body = remoteMessage.data?.get("body").toString()
    }

    // Check if message contains a notification payload. Handled client-side.
    remoteMessage?.notification?.let {
        //Log.d(TAG, "Message Notification Body: ${it.body}")
        title = remoteMessage.notification?.title.toString()
        body = remoteMessage.notification?.body.toString()
    }
    sendNotification(title, body)
}

The sendNotification function will send the notification to the device manager to display the notification to the user. You can configure the style of the notification, color, and what happens when the user clicks on the notification (intent change).

kotlin
//Send the notification to the device manager to display the notification to the user.
private fun sendNotification(title: String, body: String) {
    //Need to assign a notification id for each notification. Can be randomly generated or set.
    //Useful since it can be updated later on in the app.
    val notificationID = 101

    //Change the intent to navigate to the chat room when clicking on the notification.
    val intent = Intent(this, ChatActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    }
    val pend = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
    val channelID = getString(R.string.default_notification_channel_id)

    val notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        Notification.Builder(this@MyFirebaseMessagingService,
            channelID)
            .setContentTitle(title)
            .setContentText(body)
            .setSmallIcon(android.R.drawable.ic_dialog_info)
            .setChannelId(channelID)
            .setContentIntent(pend)
            .setAutoCancel(true)
            .build()
    } else {
       Log.d("TAG", "Build.VERSION.SDK_INT < 0. Update SDK.")
    }
    val notificationManager: NotificationManager =
        getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

    notificationManager?.notify(notificationID, notification as Notification?)
}

Step 6. Run Your Application

You are now ready to test and run your application. Sync the files by clicking on the “Sync Project with Gradle Files” button in the upper right corner of Android studio.

Select “app” as the Run/Debug Configuration, and choose the emulated device created earlier as the available device.

Click the green play button next to the available devices drop-down to run the application. The emulator will display and load the JetNews application on the virtual device.

Note: Depending on your machine/processes going on, a pop-up stating that the app is not responding may appear as the app is loading/in-use. Click on the “wait” option to allow the app to load.

Once the app is loaded, click an article to view. Click on the chat icon on the bottom toolbar to open the chat room.

You can view previous messages sent in this chat room for the article and send chat messages by click in the Type Message field, typing a message, and pressing the send button.

You will mimic another device in the chat room by using PubNub's debug console to send a message that contains a mobile push notification that alerts the in-app user. You will need at least two users registered in the database for the “default-article” channel. You will be connecting to the network from two separate sources and each source is required to connect using a user already registered in the database.

Restart the application and repeat the same process of running the application above to register another user. The usernames are the UUIDs needed for communication in the network.

Go to the debug console in the browser while you are in the chat room in the application. In the debug console, enter in the publish and subscribe keys above. You will also need to enter in the channel name of "default-article" in the channel field. Finally, enter the first UUID (username) generated from the application that already exists in the database.

Subscribe to this channel by clicking the slider on the right hand side in the debug console.

In the Android Application, create and send a message. Press the back button to be brought back to the previous article (app is in foreground).

In the debug console, you can see typing indicator messages, as see the received message get logged in the Messages section. This verifies that your application is successfully running on the PubNub Network.

You will now send a message from the Debug Console to your Android App to mimic another device. The message will contain a mobile push notification section in the payload. Once the message is sent, PubNub inspects the payload; if there is a mobile push notification payload, in this case pn_gcm, PubNub will check if the device is registered on the channel. If it is found, PubNub forwards the push to FCM. FCM will then deliver the mobile push notification to the device.

In the debug console, send a message by formatting a message in the Message section.

Format the message as a JSON object. Include the following four fields:

  1. text: The message the user wants to send.

  2. type: The type of the message - leave as default as shown below.

  3. id: The ID of the message in the PubNub Network. This must be a unique, random string. The example below shows the message with a generated id that is slightly altered from the id sent by the application.

  4. pn_gcm: The mobile push notification payload for Android devices. Please ensure that the payload header is pn_gcm, and not pn_fcm.

    1. This payload will include either (or both) of the two message types of mobile push notifications: notification and data. The type of the message is important since it defines how mobile push notifications are handled by your application:

      1. Notification messages contain a predefined set of user-visible keys, and are thought of as "display messages". They are handled automatically by FCM when the app is in the background or closed. However, notification messages are handled by the client app when the app is in the foreground.

      2. Data messages are handled by the client app, regardless if the app is in the foreground, background, or closed. Data messages contain only your user-defined custom key-value pairs, making them much more flexible.

    2. For the purpose of this tutorial, both types are will be formatted and handled exactly the same by the application. The title is the header of the mobile push notification, in this case, the UUID of the debug console user, and the body is the text or message of the mobile push notification.

Below is an example of a message to send to an Android application.

json
{
"text":"PubNub is super cool!",
 "type":"default",
 "id":"aswe234vvowasdfpqlasdf", 
 "pn_gcm": {
    "notification": {
      "title": "UUID of User",
      "body": "PubNub is super cool!"
     }
   }
}

Press the blue “Publish a Message Button” as shown in the above screenshot. In the application, a mobile push notification will appear at the top of the AVD that displays the UUID of the debug console user as the title, and the message as the body. The mobile push notification will also remain in the notification bar, and can be viewed by dragging the screen down.

Click on the mobile push notification to be brought to the application chat room. You should be able to view this message from the debug console user in the application chat room.

Send messages from the debug console when the application is completely closed (but the AVD is still running) to see similar results and when the application is in the background to be brought to the main page of the application since the intent change is handled by FCM.

You can also change the push message type from notification to data to see the mobile push notifications be handled by the client app, regardless of the app in the foreground, background, or closed.

Step 04

Congratulations - what's next?

In this tutorial, you looked at how to add mobile push notifications to an Android Kotlin chat application using PubNub. You created a Firebase project, set up a PubNub Account, configured and integrated mobile push notifications using FCM into an existing chat application, and successfully generated mobile push notifications to be received in an emulated device running the application. The complete application of this tutorial is available in the GitHub Repository, in the push-notifications branch. The source code contains everything performed in this tutorial.

Mobile push notifications are a great way to notify online and offline users about appointments, updates, previews of conversations, special offers, and so much more. Combine this with PubNub’s real time communication platform, and you have a complete application experience, giving you a way to continually engage and offer value to your users. PubNub’s Mobile Push Gateway makes it easy to send mobile push notifications to your users, by acting as a bridge between the service providers and your application, meaning you don’t have to use different frameworks and SDKs to send different types of mobile push notifications: you can do it all in one place, and let PubNub take care of the rest.

Live Demo

PubNub Push Demo

Explore our chat docs

PubNub Android Chat Documentation Home

See how PubNub compares

PubNub Vs SendbirdPubNub Vs PusherPubNub Vs Stream

Ready to get beyond Proof of Concept?

Schedule a 15 minute chat with one of our experts to get your project moving

Talk to an Expert
Keep building

Take it to production on your own keys

Create a free PubNub account to get permanent keys for this app.