Introduction To Firebase Cloud Messaging (FCM)

FCM

As everyone knows the importance of push notification in apps, we will discuss FCM (Firebase Cloud Messaging) in this article.

Let’s see what the definition of FCM is.

Firebase Cloud Messaging is a powerful API that lets you deliver the messages reliably and independent of the platform you are developing on. With the help of FCM, you can send notification and message to your client’s app. We can send instant messages of upto 4KB in size.

Before moving ahead, we will summarize how we are going to implement it.

  • We are going to develop an Android app which will display the notification.
  • Setting up FCM.
  • Saving token ID generated from android device to server using web service developed in .net
  • Sending notification message from C# code.

Android app to display notification

  • First, let us create a new Android Studio Project with an Empty Activity.
  • Once your project is loaded, click on "Firebase" from the Tools menu. With Android Studio 2.2, it is really easy to integrate Firebase into your project.

    Android
  • All the available features will get opened. Click "Cloud Messaging".

    Android
  • Click on "Set up Firebase Cloud Messaging".

    Android
  • Click on the "Connect to Firebase" button. A dialog will open.

    Android

    Android
  • Enter the project name and click on "Connect to Firebase".
  • Click on the button "Add FCM to your app" and you will see a dialog box.

    Android
  • Click on "Accept Changes". 
  • Every device generates a unique token or device id to receive notifications. And for this, we have to create a class that will extend the class FirebaseInstanceIdService. So, we have created MyFirebaseInstanceIDService.java
  • To store the generated token in SharedPreferences, we have created SharedPrefManager.java in Singleton Pattern.
  • To actually receive the notification FirebaseMessagingService is used, so we will create MyFirebaseMessagingService.java.
  • We will edit the activity_main.xml file.
  • Also, we will edit the MainActivity.java file.
  • Also, edit the AndroidManifest.xml file. 

MyFirebaseInstanceIDService.java 

  1. package com.infobytessolutions.sretailer;  
  2.   
  3. /** 
  4.  * Created by Nilesh on 05/12/2017. 
  5.  */  
  6. import android.util.Log;  
  7.   
  8. import com.google.firebase.iid.FirebaseInstanceId;  
  9. import com.google.firebase.iid.FirebaseInstanceIdService;  
  10.   
  11. public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {  
  12.   
  13.     private static final String TAG = "MyFirebaseIIDService";  
  14.   
  15.     @Override  
  16.     public void onTokenRefresh() {  
  17.   
  18.         //Getting registration token  
  19.        String refreshedToken = FirebaseInstanceId.getInstance().getToken();  
  20.   
  21.         //Displaying token on logcat  
  22.        Log.d(TAG, "Refreshed token: " + refreshedToken);  
  23.   
  24.         //calling the method store token and passing token  
  25.        storeToken(refreshedToken);  
  26.     }  
  27.   
  28.     private void storeToken(String token) {  
  29.         //saving the token on shared preferences  
  30.        SharedPrefManager.getInstance(getApplicationContext()).saveDeviceToken(token);  
  31.     }  
  32. }  

SharedPrefManager.java

  1. package com.infobytessolutions.sretailer;  
  2.   
  3. /** 
  4.  * Created by Nilesh on 05/12/2017. 
  5.  */  
  6. import android.content.Context;  
  7. import android.content.SharedPreferences;  
  8.   
  9. public class SharedPrefManager {  
  10.     private static final String SHARED_PREF_NAME = "FCMSharedPref";  
  11.     private static final String TAG_TOKEN = "tagtoken";  
  12.   
  13.     private static SharedPrefManager mInstance;  
  14.     private static Context mCtx;  
  15.   
  16.     private SharedPrefManager(Context context) {  
  17.         mCtx = context;  
  18.     }  
  19.   
  20.     public static synchronized SharedPrefManager getInstance(Context context) {  
  21.         if (mInstance == null) {  
  22.             mInstance = new SharedPrefManager(context);  
  23.         }  
  24.         return mInstance;  
  25.     }  
  26.   
  27.     //this method will save the device token to shared preferences  
  28.    public boolean saveDeviceToken(String token){  
  29.         SharedPreferences sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);  
  30.         SharedPreferences.Editor editor = sharedPreferences.edit();  
  31.         editor.putString(TAG_TOKEN, token);  
  32.         editor.apply();  
  33.         return true;  
  34.     }  
  35.   
  36.     //this method will fetch the device token from shared preferences  
  37.    public String getDeviceToken(){  
  38.         SharedPreferences sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);  
  39.         return  sharedPreferences.getString(TAG_TOKEN, null);  
  40.     }  
  41.   
  42. }  

MyFirebaseMessagingService.java

  1. package com.infobytessolutions.sretailer;  
  2.   
  3. /** 
  4.  * Created by Nilesh on 05/12/2017. 
  5.  */  
  6. import android.app.NotificationManager;  
  7. import android.app.PendingIntent;  
  8. import android.content.Context;  
  9. import android.content.Intent;  
  10. import android.media.RingtoneManager;  
  11. import android.net.Uri;  
  12. import android.support.v4.app.NotificationCompat;  
  13. import android.util.Log;  
  14.   
  15. import com.google.firebase.messaging.FirebaseMessagingService;  
  16. import com.google.firebase.messaging.RemoteMessage;  
  17.   
  18. import org.json.JSONException;  
  19. import org.json.JSONObject;  
  20.   
  21. public class MyFirebaseMessagingService extends FirebaseMessagingService {  
  22.   
  23.     private static final String TAG = "MyFirebaseMsgService";  
  24.     @Override  
  25.     public void onMessageReceived(RemoteMessage remoteMessage) {  
  26.         if (remoteMessage.getData().size() > 0) {  
  27.             Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());  
  28.             try {  
  29.                 JSONObject json = new JSONObject(remoteMessage.getData().toString());  
  30. //                sendPushNotification(json);  
  31.                sendNotification(json);  
  32.             } catch (Exception e) {  
  33.                 Log.e(TAG, "Exception: " + e.getMessage());  
  34.             }  
  35.         }  
  36.     }  
  37.       
  38.     private void sendNotification(JSONObject json) {  
  39.         Intent intent = new Intent(this, MainActivity.class);  
  40.         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
  41.         Log.e(TAG, "Notification JSON " + json.toString());  
  42.         try{  
  43.             JSONObject data = json.getJSONObject("data");  
  44.             String title = data.getString("title");  
  45.             String message = data.getString("message");  
  46.             String imageUrl = data.getString("image");  
  47.   
  48.             PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,  
  49.                     PendingIntent.FLAG_ONE_SHOT);  
  50.   
  51.             Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);  
  52.             NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)  
  53.                     .setSmallIcon(R.drawable.ic_launcher_background)  
  54.                     .setContentTitle(title)  
  55.                     .setContentText(message)  
  56.                     .setAutoCancel(true)  
  57.                     .setSound(defaultSoundUri)  
  58.                     .setContentIntent(pendingIntent);  
  59.   
  60.             NotificationManager notificationManager =  
  61.                     (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);  
  62.   
  63.             notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());  
  64.   
  65.         } catch (JSONException e) {  
  66.         Log.e(TAG, "Json Exception: " + e.getMessage());  
  67.     } catch (Exception e) {  
  68.         Log.e(TAG, "Exception: " + e.getMessage());  
  69.     }  
  70.     }  
  71. }  

activity_main.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.    xmlns:tools="http://schemas.android.com/tools"  
  4.    android:id="@+id/activity_main"  
  5.    android:layout_width="match_parent"  
  6.    android:layout_height="match_parent"  
  7.    android:paddingBottom="@dimen/activity_vertical_margin"  
  8.    android:paddingLeft="@dimen/activity_horizontal_margin"  
  9.    android:paddingRight="@dimen/activity_horizontal_margin"  
  10.    android:paddingTop="@dimen/activity_vertical_margin"  
  11.    tools:context=".MainActivity">  
  12.   
  13.     <EditText  
  14.        android:id="@+id/editTextPhoneNo"  
  15.        android:layout_width="match_parent"  
  16.        android:layout_height="wrap_content"  
  17.        android:layout_above="@+id/buttonRegister"  
  18.        android:hint="Enter Phone No"  
  19.        android:inputType="phone" />  
  20.   
  21.     <Button  
  22.        android:layout_centerVertical="true"  
  23.        android:text="Register Device"  
  24.        android:id="@+id/buttonRegister"  
  25.        android:layout_width="match_parent"  
  26.        android:layout_height="wrap_content" />  
  27.   
  28. </RelativeLayout>  

MainActivity.java

  1. package com.infobytessolutions.sretailer;  
  2.   
  3. import android.app.ProgressDialog;  
  4. import android.os.Bundle;  
  5. import android.os.StrictMode;  
  6. import android.util.Log;  
  7. import android.view.View;  
  8. import android.support.v7.app.AppCompatActivity;  
  9. import android.widget.Button;  
  10. import android.widget.EditText;  
  11. import android.widget.TextView;  
  12. import android.widget.Toast;  
  13. import com.google.firebase.messaging.FirebaseMessaging;  
  14. import org.ksoap2.SoapEnvelope;  
  15. import org.ksoap2.serialization.SoapObject;  
  16. import org.ksoap2.serialization.SoapSerializationEnvelope;  
  17. import org.ksoap2.transport.AndroidHttpTransport;  
  18. import org.xmlpull.v1.XmlPullParserException;  
  19.   
  20. import java.io.IOException;  
  21.   
  22. public class MainActivity extends AppCompatActivity  
  23.         implements View.OnClickListener {  
  24.   
  25.     //defining views  
  26.    private Button buttonRegister ;  
  27.     private TextView textViewToken;  
  28.     private EditText editTextPhoneNo;  
  29.     private ProgressDialog progressDialog;  
  30.   
  31.     @Override  
  32.     protected void onCreate(Bundle savedInstanceState) {  
  33.         super.onCreate(savedInstanceState);  
  34.         setContentView(R.layout.activity_main);  
  35.   
  36.         //getting views from xml  
  37.        editTextPhoneNo= (EditText) findViewById(R.id.editTextPhoneNo);  
  38.         buttonRegister = (Button) findViewById(R.id.buttonRegister);  
  39.   
  40.         //adding listener to view  
  41.        buttonRegister.setOnClickListener(this);  
  42.         onNewIntent(getIntent());  
  43.         FirebaseMessaging.getInstance();  
  44.     }  
  45.   
  46.     private void sendTokenToServer(){  
  47.         progressDialog = new ProgressDialog(this);  
  48.         progressDialog.setMessage("Registering Device...");  
  49.         progressDialog.show();  
  50.   
  51.         final String token = SharedPrefManager.getInstance(this).getDeviceToken();  
  52.         final String phoneNo = editTextPhoneNo.getText().toString();  
  53.   
  54.         if (token == null) {  
  55.             progressDialog.dismiss();  
  56.             Toast.makeText(this"Token not generated", Toast.LENGTH_LONG).show();  
  57.             return;  
  58.         }  
  59.         String TAG = "Update token id";  
  60.         String nameSpace="http://ws.mywebservice.com/";  
  61.         String url="http://ws.mywebservice.com/WebService.asmx";  
  62.         String SOAPACTION_getLandmark="http://ws.mywebservice.com/updateTokenID";  
  63.         String method_name_getLandmark="updateTokenID";  
  64.         // Allow internet  
  65.        if (android.os.Build.VERSION.SDK_INT > 9) {  
  66.             StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()  
  67.                     .permitAll().build();  
  68.             StrictMode.setThreadPolicy(policy);  
  69.         }  
  70.         SoapObject Request = new SoapObject(nameSpace,method_name_getLandmark);  
  71.         Request.addProperty("phoneNo",phoneNo.trim());  
  72.         Request.addProperty("TokenID",token);  
  73.         // Create a envelope  
  74.        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(  
  75.                 SoapEnvelope.VER12);  
  76.         envelope.dotNet = true;  
  77.         envelope.setOutputSoapObject(Request);  
  78.         try {  
  79.             AndroidHttpTransport transp = new AndroidHttpTransport(url);  
  80.             transp.call(SOAPACTION_getLandmark, envelope);  
  81.             SoapObject obj1 = (SoapObject) envelope.bodyIn;  
  82. //            String obj2 =(String) obj1.getProperty(0);  
  83.   
  84.             Log.d("Token:----", obj1.toString());  
  85. //            Log.d("OTP:----", obj1.getProperty(0).toString());  
  86.            Log.d(TAG, "Token finished");  
  87.         } catch (IOException e) {  
  88.             Log.d(e.getMessage(), "Token");  
  89.             e.printStackTrace();  
  90.         } catch (XmlPullParserException e) {  
  91.             e.printStackTrace();  
  92.         }  
  93.         progressDialog.dismiss();  
  94.     }  
  95.     @Override  
  96.     public void onClick(View view) {  
  97.         if (view == buttonRegister) {  
  98.             //getting token from shared preferences  
  99.            String token = SharedPrefManager.getInstance(this).getDeviceToken();  
  100.   
  101.             //if token is not null  
  102.            if (token != null) {  
  103.                 //displaying the token  
  104. //                textViewToken.setText(token);  
  105.                sendTokenToServer();  
  106.             } else {  
  107.                 //if token is null that means something wrong  
  108.                textViewToken.setText("Token not generated");  
  109.             }  
  110.         }  
  111.     }  
  112. }  

AndroidManifest.xml 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.    package="com.infobytessolutions.sretailer">  
  4.     <uses-permission android:name="android.permission.INTERNET" />  
  5.     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
  6.     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />  
  7.     <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>  
  8.     <uses-permission android:name="android.permission.WAKE_LOCK"/>  
  9.     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />  
  10.     <uses-permission android:name="android.permission.VIBRATE" />  
  11.     <permission  
  12.        android:name="${applicationId}.permission.C2D_MESSAGE"  
  13.        android:protectionLevel="signature"/>  
  14.     <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>  
  15.   
  16.     <application  
  17.        android:allowBackup="true"  
  18.        android:icon="@mipmap/ic_launcher"  
  19.        android:label="@string/app_name"  
  20.        android:roundIcon="@mipmap/ic_launcher_round"  
  21.        android:supportsRtl="true"  
  22.        android:theme="@style/AppTheme">  
  23.         <activity  
  24.            android:name=".MainActivity"  
  25.            android:label="@string/app_name"  
  26.             android:theme="@style/AppTheme.NoActionBar">  
  27.             <intent-filter>  
  28.                 <action android:name="android.intent.action.MAIN" />  
  29.   
  30.                 <category android:name="android.intent.category.LAUNCHER" />  
  31.             </intent-filter>  
  32.         </activity>  
  33.         <service  
  34.            android:name=".MyFirebaseInstanceIDService">  
  35.             <intent-filter>  
  36.                 <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>  
  37.             </intent-filter>  
  38.         </service>  
  39.         <service  
  40.            android:name=".MyFirebaseMessagingService">  
  41.             <intent-filter>  
  42.                 <action android:name="com.google.firebase.MESSAGING_EVENT"/>  
  43.             </intent-filter>  
  44.         </service>  
  45.     </application>  
  46.   
  47. </manifest>    

Setting up FCM in Firebase console

  • Now, put your app name and select your country.

    FCM
  • Now, click "Add Firebase to your Android app".

    FCM
  • Now, you have to enter your project's package name and click on "Add App".

    FCM
  • After this, you will get a google-services.json file by going to the Project Settings.

    FCM

  • Go to the root level build.gradle file and add the following code. 
    1. buildscript {  
    2.     repositories {  
    3.         jcenter()  
    4.         google()  
    5.     }  
    6.     dependencies {  
    7.         classpath 'com.android.tools.build:gradle:3.0.1'  
    8.         //Add this line  
    9.         classpath 'com.google.gms:google-services:3.1.0'  
    10.     }  
    11. }  
    12. allprojects {  
    13.     repositories {  
    14.         jcenter()  
    15.         google()  
    16.     }  
    17. }  
    18. task clean(type: Delete) {  
    19.     delete rootProject.buildDir  
  • Inside app level build.gradle file, make the following changes.
    1. apply plugin: 'com.android.application'  
    2.   
    3. android {  
    4.     compileSdkVersion 26  
    5.     defaultConfig {  
    6.         applicationId "com.infobytessolutions.sretailer"  
    7.        minSdkVersion 21  
    8.         targetSdkVersion 26  
    9.         versionCode 1  
    10.         versionName "1.0"  
    11.        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"  
    12.    }  
    13.     buildTypes {  
    14.         release {  
    15.             minifyEnabled false  
    16.            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
    17.        }  
    18.     }  
    19. }  
    20.   
    21. dependencies {  
    22.     implementation fileTree(dir: 'libs', include: ['*.jar'])  
    23.     implementation 'com.android.support:appcompat-v7:26.1.0'  
    24.    implementation 'com.android.support:support-v4:26.1.0'  
    25.    implementation 'com.android.support:design:26.1.0'  
    26.    implementation 'com.android.support.constraint:constraint-layout:1.0.2'  
    27.    implementation 'com.google.firebase:firebase-messaging:10.2.1'  
    28.    testImplementation 'junit:junit:4.12'  
    29.    androidTestImplementation 'com.android.support.test:runner:1.0.1'  
    30.    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'  
    31.    compile files('libs/ksoap2-android-assembly-2.5.8-jar-with-dependencies.jar')  
    32. }  
    33. apply plugin: 'com.google.gms.google-services'  
  • Now, sync your project.

Saving token ID generated from android device to server using web service developed in .net

If you have knowledge of creating a web service, then add the below code to it, else learn it by searching on Google because our main focus in this article is FCM.

  • We have table “Customers” with phoneNo and tokenID columns.
  • In my case, phone number values are already stored.
    1. [WebMethod]  
    2. public void updateTokenID(string phoneNo, string TokenID) {  
    3.     int count = db.Customers.Where(c => c.PhoneNo == phoneNo).Count();  
    4.     Customer cust = db.Customers.FirstOrDefault(c => c.PhoneNo == phoneNo);  
    5.     cust.TokenID = TokenID;  
    6.     db.SubmitChanges();  
    7. }  

To run web service properly in Android, you have to add ksoap2-android-assembly-2.5.8-jar-with-dependencies.jar in Android project.

Also, have a look at mainactivity.java file's sendTokenToServer() method where web service call is handled.

Sending notification message from C# code

Create a new C# project and add the following class and call it in any event like button.

  1. public class FCMPushNotification {  
  2.     public FCMPushNotification() {  
  3.         // TODO: Add constructor logic here  
  4.     }  
  5.     public bool Successful {  
  6.         get;  
  7.         set;  
  8.     }  
  9.     public string Response {  
  10.         get;  
  11.         set;  
  12.     }  
  13.     public Exception Error {  
  14.         get;  
  15.         set;  
  16.     }  
  17.     public FCMPushNotification SendNotification(string _title, string _message, string _topic, string deviceId) {  
  18.         FCMPushNotification result = new FCMPushNotification();  
  19.         try {  
  20.             result.Successful = true;  
  21.             result.Error = null;  
  22.             // var value = message;  
  23.             string serverKey = "Your server key";  
  24.             string senderId = "your sender id";  
  25.             var requestUri = "https://fcm.googleapis.com/fcm/send";  
  26.             WebRequest webRequest = WebRequest.Create(requestUri);  
  27.             webRequest.Method = "POST";  
  28.             webRequest.Headers.Add(string.Format("Authorization: key={0}", serverKey));  
  29.             webRequest.Headers.Add(string.Format("Sender: id={0}", senderId));  
  30.             webRequest.ContentType = "application/json";  
  31.             var data = new {  
  32.                 to = deviceId, // this if you want to test for single device  
  33.                     //                        to = "/topics/" + _topic, // this is for topic  
  34.                     priority = "high",  
  35.                     notification = new {  
  36.                         title = _title,  
  37.                             body = _message,  
  38.                             show_in_foreground = "True",  
  39.                             icon = "myicon"  
  40.                     }  
  41.             };  
  42.             var serializer = new JavaScriptSerializer();  
  43.             var json = serializer.Serialize(data);  
  44.             Byte[] byteArray = Encoding.UTF8.GetBytes(json);  
  45.             webRequest.ContentLength = byteArray.Length;  
  46.             using(Stream dataStream = webRequest.GetRequestStream()) {  
  47.                 dataStream.Write(byteArray, 0, byteArray.Length);  
  48.                 using(WebResponse webResponse = webRequest.GetResponse()) {  
  49.                     using(Stream dataStreamResponse = webResponse.GetResponseStream()) {  
  50.                         using(StreamReader tReader = new StreamReader(dataStreamResponse)) {  
  51.                             String sResponseFromServer = tReader.ReadToEnd();  
  52.                             result.Response = sResponseFromServer;  
  53.                         }  
  54.                     }  
  55.                 }  
  56.             }  
  57.         } catch (Exception ex) {  
  58.             result.Successful = false;  
  59.             result.Response = null;  
  60.             result.Error = ex;  
  61.         }  
  62.         return result;  
  63.     }  
  64. }  

How to get server key and sender id

  • Go to Firebase Console and inside your project settings, go to Cloud Messaging tab.
  • You will find the Cloud Messaging Server Key here, copy it.

    Cloud Messaging Server

So that’s it. Though a few mobile phones will kill this notification when application is killed. I am working on it too. Feel free to comment, I would like to guide you if you are stuck somewhere and also like to learn more from you guys.