Welcome to another post friends and in this post we are going to learn about Android Push Notifications. We have already learned it in the past but things have changed now, so it was very important that I should post the updated method.
I hope you all are aware about Notification and Push Notification so we are not going into the theoretical things, instead we will create a very simple application to demonstrate how we can implement push notification in our android application using Firebase Cloud Messaging.
If you want to see what we will be building in this post, you can download the APK from the below given link.
Now if you want to learn how I built this App then let’s move ahead.
Table of Contents
Android Push Notification Tutorial – Video Series
- I have a complete video series of this Android Push Notification Tutorial, you can check. This series uses Java Programming Language.
But if you don’t want videos or you want to learn with Kotlin, then let’s move ahead.
The Android Project
As always we need to create an Android Studio Project to create this app.
- Open Android Studio and create a new project. Select an Empty Activity while creating the project. And also make sure to check the Kotlin Support (Because now onwards I will be using Kotlin for all the coming projects).
- Once the project is loaded and gradle has done its task (OMG you did it) you need the User Interface.
Designing the Interface
I have not focused much on interface designing but you are free to design whatever you want. But if you are very new into these things, below is my xml code for you that you can directly copy past to design the Main Activity (The only activity we have in this application).
- Replace the code of your existing activity_main.xml file with the following 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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:padding="16dp" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:textSize="42sp" android:textAppearance="@style/Base.TextAppearance.AppCompat.Headline" android:layout_centerHorizontal="true" android:textAlignment="center" android:text="Android Push Notification Tutorial" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/textView" app:fontFamily="sans-serif-thin"/> <TextView android:layout_centerHorizontal="true" android:layout_below="@id/textView" android:textSize="18sp" android:padding="4dp" android:background="@color/colorPrimary" android:textAlignment="center" android:textColor="#ffffff" android:textAppearance="@style/Base.TextAppearance.AppCompat.Headline" android:text="www.simplifiedcoding.net" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/textView2" app:fontFamily="sans-serif-condensed-light"/> <LinearLayout android:layout_centerInParent="true" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <ProgressBar android:id="@+id/progressbar" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:layout_gravity="center_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:text="Please wait until you see the token here" android:layout_marginBottom="10dp" android:layout_gravity="center_horizontal" android:id="@+id/textViewMessage" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@+id/textViewToken" android:textAlignment="center" android:layout_centerInParent="true" android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" android:text="7uug2rYyXSxVHJOMQDer3gAZUDhFYh4STO15sPzopWIbeHOkHJsI8BKwKEkhhBszMsyXYrQwkOOBu97q" android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:id="@+id/buttonCopyToken" android:layout_gravity="center_horizontal" android:textAllCaps="false" android:layout_marginTop="12dp" android:text="Copy Token" android:background="@color/colorPrimary" android:layout_width="wrap_content" android:layout_height="30dp"/> </LinearLayout> <Button android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:textAllCaps="false" android:text="Open Dashboard" android:id="@+id/buttonOpenDashboard" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout> |
- The above code will generate the following interface. (Not so bad haan 😉 )
- So we are done with the designing. Now let’s move ahead.
Adding Firebase
For push notification we are going to use Firebase Cloud Messaging. So the first thing that is needed is the Firebase itself. And you know what adding firebase is very easy nowdays.
- In Android Studio go to Tools -> Firebase (As shown in the image below).
- When you click on the Firebase, it opens an window on the right displaying all the firebase features that you have. This time we care about Cloud Messaging.
- Now, you need to follow two step, the first one is connecting the app to Firebase.
- When you click on Connect to Firebase, it opens a Connection Dialog and here you can see all the firebase project you have. So you can either connect to an existing firebase project from your account or you can create a new project. Here I am going to create a new Firebase Project.
- It might take a couple of minute to connect to the firebase, so wait and once it is finished we need to perform the second step which is adding the firebase dependencies.
- To add dependencies you just need to click on the button Add FCM to your app.
- Now it will show you what are the changes it is going to make (Don’t worry about anything, just accept the changes).
- Now it will take a few seconds (or minutes) to download the dependencies and sync your project with gradle.
Generating Registration Token (or FCM Token)
Before going ahead I will tell you a little theoretical thing. We basically have two methods to send the notifications.
- Send to an individual device or a Group: Every device is identified uniquely with a registration token. We can use this token to send the notification to an individual device. We can also make a group of registration tokens to send notification to multiple devices.
- Send to a Topic: You can let your users subscribe to a topic. For example we have a topic X. Now when you send the notification to topic X, all the subscriber of this topic will receive the notification. In this method we do not need to identify the device uniquely so the registration token is not required.
In this post I am covering the first method only and for this we need the Registration Token.
So let’s learn how do we generate a Registration Token for FCM (Firebase Cloud Messaging).
- Come to MainActivity.kt and write the following 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 |
package net.simplifiedcoding import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import com.google.android.gms.tasks.OnCompleteListener import com.google.firebase.iid.FirebaseInstanceId import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) FirebaseInstanceId.getInstance().instanceId .addOnCompleteListener(OnCompleteListener { task -> //hiding the progressbar progressbar.visibility = View.INVISIBLE if (!task.isSuccessful) { //displaying the error if the task is unsuccessful textViewToken.text = task.exception?.message //stopping the further execution return@OnCompleteListener } //Getting the token if everything is fine val token = task.result?.token textViewMessage.text = "Your FCM Token is:" textViewToken.text = token }) } } |
- Now you can run your application to see the generated token.
- Bingo! you can see we have the token. We will code the Open Dashboard button later (After completing the dashboard). Right now we need to create the notification.
Creating Notification Channel
To display notification we need a notification channel. And remember this notification channel thing is supported only from Android Oreo, so we need to put this condition in our code as well.
- Again inside MainActivity.kt you need to do the following changes.
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 |
package net.simplifiedcoding import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import com.google.android.gms.tasks.OnCompleteListener import com.google.firebase.iid.FirebaseInstanceId import kotlinx.android.synthetic.main.activity_main.* import android.app.NotificationManager import android.app.NotificationChannel import android.os.Build class MainActivity : AppCompatActivity() { //Defined the required values companion object { const val CHANNEL_ID = "simplified_coding" private const val CHANNEL_NAME= "Simplified Coding" private const val CHANNEL_DESC = "Android Push Notification Tutorial" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) FirebaseInstanceId.getInstance().instanceId .addOnCompleteListener(OnCompleteListener { task -> progressbar.visibility = View.INVISIBLE if (!task.isSuccessful) { textViewToken.text = task.exception?.message return@OnCompleteListener } val token = task.result?.token textViewMessage.text = "Your FCM Token is:" textViewToken.text = token }) //creating notification channel if android version is greater than or equals to oreo if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT) channel.description = CHANNEL_DESC val manager = getSystemService(NotificationManager::class.java) manager.createNotificationChannel(channel) } } } |
- Our notification channel is now ready. We can use this channel to display notifications. So the next task is to create a class that will display the notifications.
Building Notification
- We are going to create a function that will display the notification. So whenever a notification is received we will call this function to display the notification in the device. For this I am going to use a Kotlin object.
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 |
package net.simplifiedcoding import android.content.Context import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import android.app.PendingIntent import android.content.Intent object NotificationHelper { fun displayNotification(context: Context, title: String, body: String) { val intent = Intent(context, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity( context, 100, intent, PendingIntent.FLAG_CANCEL_CURRENT ) val mBuilder = NotificationCompat.Builder(context, MainActivity.CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(title) .setContentText(body) .setContentIntent(pendingIntent) .setAutoCancel(true) .setPriority(NotificationCompat.PRIORITY_HIGH) val mNotificationMgr = NotificationManagerCompat.from(context) mNotificationMgr.notify(1, mBuilder.build()) } } |
- In the above code you can see we have a function taking the details of the notification (title and body). This function will generate and display the notification when we call it.
- Now we just need to receive the notification and call this method to display it.
Receiving Notification
- To receive the notification we will create a FirebaseMessagingService. So create a new class named MyFirebaseMessagingService and inherit the class FirebaseMessagingService. Inside this class we can override the function onMessageReceived(). This function will be called whenever we receive a notification.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package net.simplifiedcoding import com.google.firebase.messaging.RemoteMessage import com.google.firebase.messaging.FirebaseMessagingService class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage?) { super.onMessageReceived(remoteMessage) if (remoteMessage!!.notification != null) { val title = remoteMessage.notification!!.title val body = remoteMessage.notification!!.body NotificationHelper.displayNotification(applicationContext, title!!, body!!) } } } |
- You can see inside the function onMessageReceived() we are fetching the title and the body of the notification and then we are calling the function that we created to display notification.
- Now we just need to define our FirebaseMessagingService inside AndroidManifest.xml.
- So go to AndroidManifest.xml and inside the application tag put this FirebaseMessagingService.
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 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.simplifiedcoding"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- Defining our FirebaseMessagingService --> <service android:name=".MyFirebaseMessagingService"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT"/> </intent-filter> </service> </application> </manifest> |
- Now everything is done for our client side (Android App). Now we need a web panel from where we can send the notification.
The Push Notification Sender
We have done with the client side which is our Android Application. Our android app will receive and display the notification. But the point is the app can only receive the notification when we sends it. So the next requirement is of a Panel from where we can send the notification. For this I will create a web panel, where we can input the device token, the title and the body of the notification and then we can press a button to send the notification.
To build this thing, I am going to use NodeJS and I will deploy the app in firebase using firebase functions.
To go ahead you need NodeJS and I am assuming that you already have it. (To confirm the node installation in command prompt you can run the command npm –version).
Installing Firebase CLI
As I told you we are going to use Firebase Functions, and to use it we need Firebase CLI. You can get Firebase CLI very easily if you have NodeJS installed. Just open the command prompt and run the below command.
1 2 3 |
npm install -g firebase-tools |
After installing Firebase CLI you also need to login with your google account. And to login you need to run the command firebase login (as shown in the image below).
Creating a NodeJS Project
- For this web project simply create a directory (folder). I created a directory named SimplifiedCodingNotificationSender and then in command prompt we need to go inside the created directory. (As you can see in the screenshot).
- Now we need to run the command firebase init functions. Then follow the steps as shown in the below image (till it says do you want to install npm dependencies).
- Now open your project in any Editor (I am using Visual Studio Code).
- Remember we will be working inside the functions directory.
Adding Service Account Private Key
- Before doing anything we need a Service Account file that contains the private key used to authenticate firebase features. And we need to put it inside the functions folder.
- So open firebase console and go to your project settings and then service account. Here you can generate a new private key.
- When you generate a private key you will get a json file containing your private key.
- We need to put this file inside the functions directory of our NodeJS project. As you can see the name of the file is very big and complicated so I will change the name to service-account.json.
- Once you put this file inside your functions folder your project will look like this.
Adding Required Dependencies
- We will be using express for building the API to send notification. So first come inside package.json file and inside dependencies block add everything that is shown in the below code. Or you can simply copy paste the whole thing to your package.json file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
{ "name": "functions", "description": "Cloud Functions for Firebase", "scripts": { "serve": "firebase serve --only functions", "shell": "firebase functions:shell", "start": "npm run shell", "deploy": "firebase deploy --only functions", "logs": "firebase functions:log" }, "dependencies": { "firebase-admin": "~7.0.0", "firebase-functions": "^2.2.0", "body-parser": "^1.18.3", "express": "^4.16.4", "googleapis": "^36.0.0", "request": "^2.88.0" }, "private": true } |
- After adding the dependencies you need to run the command npm install (Make sure you are inside the functions directory when you are running npm install).
Building REST API
- First come inside index.js and write the following 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 62 63 64 65 66 67 68 69 70 71 |
const functions = require('firebase-functions'); var {google} = require('googleapis'); var MESSAGING_SCOPE = "https://www.googleapis.com/auth/firebase.messaging"; var SCOPES = [MESSAGING_SCOPE]; var express = require('express'); var app = express(); var bodyParser = require('body-parser'); var router = express.Router(); var request = require('request'); app.use(bodyParser.urlencoded({extended:true})); app.use(bodyParser.json()); router.post('/send', function(req, res){ getAccessToken().then(function(access_token){ var title = req.body.title; var body = req.body.body; var token = req.body.token; request.post({ headers:{ Authorization: 'Bearer '+access_token }, url: "https://fcm.googleapis.com/v1/projects/<your-firebase-project-id>/messages:send", body: JSON.stringify( { "message":{ "token" : token, "notification" : { "body" : body, "title" : title, } } } ) }, function(error, response, body){ res.end(body); console.log(body); }); }); }); app.use('/api', router); function getAccessToken(){ return new Promise(function(resolve, reject){ var key = require("./service-account.json"); var jwtClient = new google.auth.JWT( key.client_email, null, key.private_key, SCOPES, null ); jwtClient.authorize(function(err, tokens){ if(err){ reject(err); return; } resolve(tokens.access_token); }); }); } exports.api = functions.https.onRequest(app); |
- In the above code make sure you change the url value (https://fcm.googleapis.com/v1/projects/<your-project-id>/messages:send) and put your own project id.
- After this come to firebase.json and write the following inside.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "hosting": { "public": "public", "rewrites":[ { "source":"/api/send", "function":"api" } ] } } |
- Here we are simply defining that call the function api of index.js when the url /api/send is called.
Deploying Firebase Functions
Our API is ready and now we can deploy it to Firebase. And deploying is very easy you just need to run the command firebase deploy. But we cannot deploy firebase functions without firebase hosting.
- So first initialize firebase hosting in your project as well.
- To initialize firebase hosting run the command firebase init hosting. (See the image for reference).
- After initializing hosting make sure your firebase.json is same as it was. If it is changed then you again need to change it as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "hosting": { "public": "public", "rewrites":[ { "source":"/api/send", "function":"api" } ] } } |
- Now run the command firebase deploy.
- As you can see our API is deployed successfully. Now if you want you can test it on postman by putting /api/send at the end of the hosting URL.
- In my case the URL is: https://simplifiedcodingnotifica-cdd38.firebaseapp.com/api/send
- The API is working fine, now we just need an interface to call the API. We will create the interface in Firebase Hosting.
Building Notification Sender
- In your project come inside index.html (Inside the public folder) and write the following code inside.
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 |
<!DOCTYPE html> <html> <head> <title>Android Push Notification Tutorial by Simplified Coding</title> </head> <body> <header> <h1>Android Push Notification Tutorial <span>By <a href="https://www.simplifiedcoding.net">Simplified Coding</a></span></h1> </header> <div> <form method="POST" action="https://simplifiedcodingnotifica-cdd38.firebaseapp.com/api/send"> <div> <label for="token">Token</label><br /> <input type="text" name="token" id="token" placeholder="cDj56lB8Oc4:APA91bExf-CMtLn2dFG7Hd72K_YiF6JZQsTEl17i6dLzqpwC67jcUn_UL1SMLChDjPM2wqtBsRQOkXpawUR7ZUT9ObD88oQs_q_i-mYxb5Q51KsSqNf8mhSxWuCsPlxpPcayZ5Xd5WMK"/> </div> <div> <label for="title">Title</label><br /> <input type="text" name="title" id="title" placeholder="Hey there this is a nice notification"/> </div> <div> <label for="body">Body</label><br /> <input type="text" name="body" id="body" placeholder="More details about your notification" /> </div> <div> <button>Send</button> </div> </form> </div> </body> </html> |
- Its just a normal HTML without CSS. You can apply some CSS in it if you think it is looking ugly 😉 .
- But make sure you change the URL to your own URL in the action attribute of form.
Now again run the command firebase deploy to deploy the thing we created.
You can check my panel here. (I have applied some CSS as well).
One thing we missed to code in our android app that is the Open Dashboard Button.
Now we have the URL of the dashboard so we can code it. So go back to Android Studio and modify MainActivity.kt as below. We will also code the Copy Token button.
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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
package net.simplifiedcoding import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import com.google.android.gms.tasks.OnCompleteListener import com.google.firebase.iid.FirebaseInstanceId import kotlinx.android.synthetic.main.activity_main.* import android.app.NotificationManager import android.app.NotificationChannel import android.os.Build import android.util.Log import android.content.Intent import android.net.Uri import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.widget.Toast class MainActivity : AppCompatActivity() { //Defined the required values companion object { const val CHANNEL_ID = "simplified_coding" private const val CHANNEL_NAME= "Simplified Coding" private const val CHANNEL_DESC = "Android Push Notification Tutorial" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) FirebaseInstanceId.getInstance().instanceId .addOnCompleteListener(OnCompleteListener { task -> progressbar.visibility = View.INVISIBLE if (!task.isSuccessful) { textViewToken.text = task.exception?.message return@OnCompleteListener } val token = task.result?.token textViewMessage.text = "Your FCM Token is:" textViewToken.text = token Log.d("Token", token) }) //creating notification channel if android version is greater than or equals to oreo if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT) channel.description = CHANNEL_DESC val manager = getSystemService(NotificationManager::class.java) manager.createNotificationChannel(channel) } //opening dashboard buttonOpenDashboard.setOnClickListener { val i = Intent(Intent.ACTION_VIEW) i.data = Uri.parse("http://bit.ly/2GEIPlu") startActivity(i) } //copying the token buttonCopyToken.setOnClickListener { val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("token", textViewToken.text) clipboard.primaryClip = clip Toast.makeText(this@MainActivity, "Copied", Toast.LENGTH_LONG).show() } } } |
- Everything is done now and we can test our application.
Testing Android Push Notification
- You can see here that it is working absolutely fine.
Android Push Notification Tutorial Source Code Download
- If you are having any trouble following the post then you can get my source code from the GitHub. The links are given below.
Source Code for this Post
Source Code for YouTube Course
So thats all for this Android Push Notification Tutorial friends. I hope you find this post helpful. If you are facing any problems just comment it here and I will try to help you. And please support me by sharing this post with your friends. Thank You 🙂