Firebase Cloud Messaging for Remote Push Notifications on Android with Xamarin

Push notifications—being one of the most integral parts of a mobile application—should be one of the first things you configure while building your mobile app. This blog will help you get you familiar with the fundamentals of setting up push notifications in your Xamarin.Android project using Firebase.

Overview:

Firebase Cloud Messaging (FCM) is a cross-platform service that handles the sending, routing and queueing of messages between server applications and mobile apps.

FCM is the replacement to Google Cloud Messaging (GCM), and it’s built on Google Play Services.

You can check out how to configure an Android app on the Firebase console in this detailed post by Microsoft:

To get started, head to the Firebase console, log in, and create a new Android project. You’ll be asked to enter your package name, which you can find in your AndroidManifest.xml file.

Setting up Firebase Cloud Messaging (Console)

Before you can use FCM services in your app, you must create a new project (or import an existing project) via the Firebase Console. Use the following steps to create a Firebase Cloud Messaging project for your app:

1. Sign into the Firebase Console with your Google account (Gmail) and click Add project:

If you have an existing project, add a new Android Project there.

2. In the Add a project dialog box, enter the name of your project, accept all terms and conditions, and click CREATE PROJECT. In the following example, a new project called XamarinFCM is created:

3. In the Firebase Console Overview, click on the Android icon to add Firebase to your Android app:

4. On the next screen, enter the package name of your app. In this example, the package name is com.xamarin.fcmexample. This value must match the package name of your Android app. An app nickname can also be entered in the App nickname field:

If your app uses dynamic links, invites, or Google Auth, you must also enter your debug signing certificate. For more information about locating your signing certificate, check out the documentation on finding your Keystore’s MD5 or SHA1 Signature. In this example, the signing certificate is left blank, since it’s not mandatory.

5. Download the google-services.json file, as you’ll need it while configuring Firebase on your mobile application. Click Next

6. Firebase SDK integration through gradle is a native Android Studio thing, so we can skip the sync and move on to the next step.

7. Run your app to verify install is a step to take when your application is all set up and ready to run, and since we’re not there yet, we shall select skip this step for now. You can always complete this verification step later from your project settings.

8. Now you should be all set to start working on the mobile app side. Also, note that you can always click on the project name to change settings (for settings that allow changes in them, e.g., to add the SHA1 key).

Points to Remember

  • A Server API key and a Client ID are automatically generated for the app. This information is packaged in a google-services.json file that’s automatically downloaded when you click on the download button at STEP 5 (Setup), Be sure to save this file in a safe place.
  • Your Package Name and your google-services.json file are linked to each other—the package name is an integral part of the JSON file, and it’s what links your mobile application to Firebase Cloud, so every package name creates a different JSON file. Therefore, make sure you don’t mismatch these, as that would cause your push notifications to break and not transmit to devices.
  • In case you have to change your mobile app package name after adding it and configuring it to Firebase, you’ll have to create a new project based on that package name and do a new setup as shown above.

Setting up Firebase Cloud Messaging (Client/Mobile)

1. Setting the package name

In the Console Setup, you specified a package name for the FCM-enabled app. This package name also serves as the application ID that’s associated with the API key.

Configure the app to use this package name in your AndroidManifest.xml file. While updating the Android Manifest, also check to be sure that the Internet permission is enabled.

2. Add the Xamarin Google Play Services

Because Firebase Cloud Messaging depends on Google Play Services, the Xamarin Google Play Services — Base NuGet package must be added to the Xamarin.Android project. You will need version 29.0.0.2 or later.

If you get an error during installation of the NuGet, close the project, open it again, and retry the NuGet installation. When you install, all of the necessary dependencies are also installed.

3. Add the Xamarin Firebase Messaging

To receive messages from FCM, the Xamarin Firebase — Messaging NuGet package must be added to the app project. Without this package, an Android application cannot receive messages from FCM servers. When you install,Xamarin.Firebase.Messaging, all of the necessary dependencies are also installed.

Next, edit MainActivity.cs and add the following using statements:

using Firebase.Messaging;
using Firebase.Iid;
using Android.Util;

Firebase Messaging& Iid that are comes withXamarin.Firebase.Messaging NuGet package makes firebase functionalities available to your project.

Android.Util adds logging functionality that will be used to observe transactions with Firebase Messaging Service (FMS).

4. Add the Google Services JSON

The next step is to add the google-services.json file to the root directory of your project:

Visual Studio:

  1. Copy google-services.json to the project folder.
  2. Add google-services.json to the app project (click Show All Files in the Solution Explorer, right-click google-services.json, then select Include in Project).
  3. Select google-services.json in the Solution Explorer window.
  4. In the Properties panel, set the Build Action to GoogleServicesJson:

Note:

If the GoogleServicesJson build action isn’t shown, save and close the solution, then reopen it.

Visual Studio for MAC

  1. Copy google-services.json to the project folder.
  2. Add google-services.json to the app project.
  3. Right-click google-services.json.
  4. Set the Build Action to GoogleServicesJson:

5. Check for Google Play Services & create a notification channel

Google recommends that Android apps check for the presence of the Google Play Services APK before accessing Google Play Services features (for more information, see Check for Google Play services).

Edit MainActivity.cs and add the following instance variables to the MainActivity class:

static readonly string TAG = "MainActivity"; 
internal static readonly string CHANNEL_ID = "my_notification_channel";
internal static readonly int NOTIFICATION_ID = 100;

The variables CHANNEL_ID and NOTIFICATION_ID will be used in the method CreateNotificationChannel that will be added to MainActivity later on in this walkthrough.

In the following example, the OnCreate method will verify that Google Play Services is available before the app attempts to use FCM services. Add the following method to the MainActivity class:

public bool IsPlayServicesAvailable () 
{    
 int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable (this);     if (resultCode != ConnectionResult.Success)    
 {        
    if (GoogleApiAvailability.Instance.IsUserResolvableError (resultCode))           
  msgText.Text = GoogleApiAvailability.Instance.GetErrorString (resultCode);       
  else        
  {           
  //This device is not supported           
  Finish (); // Kill the activity if you want.         
  }      
   return false;     
 }     
 else     
 {         
  //Google Play Services is available.         
  return true;     
 } 
}

This code checks the device to see if the Google Play Services APK is installed. If it’s not installed, it returns false and if it is installed, it returns true, so accordingly you can ask your user to download the GooglePlayServices APK from the Google Play Store (or to enable it in the device’s system settings).

Apps that are running on Android 8.0 (API level 26) or higher must create a notification channel for publishing their notifications. Add the following method to the MainActivity class, which will create the notification channel (if necessary):

void CreateNotificationChannel()
    {
        if (Build.VERSION.SdkInt < BuildVersionCodes.O)
        {
            // Notification channels are new in API 26 (and not a part of the
            // support library). There is no need to create a notification 
            // channel on older versions of Android.
            return;
        }

        var channel = new NotificationChannel(CHANNEL_ID, "FCM Notifications", NotificationImportance.Default)
                      {
                          Description = "Firebase Cloud Messages appear in this channel"
                      };

        var notificationManager = (NotificationManager) GetSystemService(NotificationService);
        notificationManager.CreateNotificationChannel(channel);
    }

Add the following code in the OnCreate method:

protected override void OnCreate (Bundle bundle) 
{   
  base.OnCreate (bundle);    
  SetContentView (Resource.Layout.Main);   
  IsPlayServicesAvailable (); //You can use this method to check if play services are available.
  CreateNotificationChannel();
 }

IsPlayServicesAvailable is called at the end of OnCreate so that the Google Play Services check runs each time the app starts. The method CreateNotificationChannel is called to ensure that a notification channel exists for devices running Android 8 or higher. If your app has the OnResume method, it should call IsPlayServicesAvailable from OnResume as well. Completely rebuild and run the app.

6. Add the instance ID receiver

The next step is to add a service that extends FirebaseInstanceIdService to handle the creation, rotation, and updating of Firebase registration tokens. The FirebaseInstanceIdService service is required for FCM to be able to send messages to the device. When FirebaseInstanceIdService is added to the client app, the app will automatically receive FCM messages and display them as notifications whenever the app is backgrounded.

7. Declare the receiver in the Android Manifest

Edit AndroidManifest.xml and insert the following <receiver> elements into the <application>section:

<receiver    android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
    android:exported="false" />
<receiver
    android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
    android:exported="true"
    android:permission="com.google.android.c2dm.permission.SEND">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="${applicationId}" />
    </intent-filter>
</receiver>

This XML does the following:

  • Declares a FirebaseInstanceIdReceiver, an implementation that provides a unique identifier for each app instance. This receiver also authenticates and authorizes actions.
  • Declares an internal FirebaseInstanceIdInternalReceiver, an implementation used to start services securely.
  • The app ID is stored in the google-services.json file that was added to the project. The Xamarin.Android Firebase bindings will replace the token ${applicationId} with the app ID; no additional code is required by the client app to provide the app ID.

The FirebaseInstanceIdReceiver is a WakefulBroadcastReceiver that receives FirebaseInstanceId and FirebaseMessaging events and delivers them to the class that you derive from FirebaseInstanceIdService.

8. Implement the Firebase Instance ID Service

The work of registering the application with FCM is handled by the custom FirebaseInstanceIdService service that you provide. FirebaseInstanceIdService performs the following steps:

  1. Uses the Instance ID API to generate security tokens that authorize the client app to access FCM and the app server. In return, the app gets back a registration token from FCM.
  2. Forwards the registration token to the app server if the app server requires it.

Add a new file called MyFirebaseIIDService.cs and replace its template code with the following:

using System;
using Android.App;
using Firebase.Iid;
using Android.Util;
namespace MyProj
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
    public class MyFirebaseIIDService : FirebaseInstanceIdService
    {
        const string TAG = "MyFirebaseIIDService";
        public override void OnTokenRefresh()
        {
            var refreshedToken = FirebaseInstanceId.Instance.Token;
            Log.Debug(TAG, "Refreshed token: " + refreshedToken);
            SendRegistrationToServer(refreshedToken);
        }
        void SendRegistrationToServer(string token)
        {
            // Add custom implementation, as needed. 
        }
    }
}

This service implements theOnTokenRefresh method that is invoked when the registration token is initially created or changed. When OnTokenRefresh runs, it retrieves the latest token from the FirebaseInstanceId.Instance.Token property (which is updated asynchronously by FCM). In this example, the refreshed token is logged so that it can be viewed in the output window:

var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(TAG, "Refreshed token: " + refreshedToken);

OnTokenRefresh is invoked infrequently: it’s used to update the token under the following circumstances:

  • When the app is installed or uninstalled.
  • When the user deletes app data.
  • When the app erases the Instance ID.
  • When the security of the token has been compromised.

According to Google’s Instance ID documentation, the FCM Instance ID service will request that the app refresh its token periodically (typically, every 6 months).

Types of Notification :

Following are the different types of notifications available

  1. Background notifications
  2. Foreground notifications
  3. Topic-based Messages

Background notifications:

First, you need to get the token generated by Firebase in the FirebaseInstanceIdService inheriting service class. Once you have the token, sign into the Firebase Console, select your project, click Notifications, and click SEND YOUR FIRST MESSAGE:

On the Compose Message page, enter the message text and select Single device. Copy the instance ID token from the IDE output window and paste it into the FCM registration token field:

On the Android device (or emulator), background the app by tapping the Android Overview button and touching the home screen. When the device is ready, click SEND MESSAGE in the Firebase Console:

When the review message dialog is displayed, click SEND. The notification icon should appear in the notification area of the device (or emulator):

Open the notification icon to view the message. The notification message should be exactly what was typed into the message text field of the Firebase Console:

In the previous example, the notification icon is set to the application icon. The following XML configures a custom default icon for notifications. Android displays this custom default icon for all notification messages where the notification icon is not explicitly set.

To add a custom default notification icon, add your icon to the Resources/drawable directory, edit AndroidManifest.xml, and insert the following <meta-data> element into the <application>section:

<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_stat_ic_notification" />

In this example, the notification icon that resides at Resources/drawable/ic_stat_ic_notification.png will be used as the custom default notification icon. If a custom default icon is not configured in AndroidManifest.xml and no icon is set in the notification payload, Android uses the application icon as the notification icon (as seen in the notification icon screenshot above).

Foreground notifications:

To receive notifications in foregrounded apps, you must implement FirebaseMessagingService. This service is also required for receiving data payloads and for sending upstream messages. The following examples illustrate how to implement a service that extends FirebaseMessagingService – the resulting app will be able to handle remote notifications while it’s running in the foreground.

Implement FirebaseMessagingService

The FirebaseMessagingService service is responsible for receiving and processing the messages from Firebase. Each app must subclass this type and override the OnMessageReceived to process an incoming message. When an app is in the foreground, the OnMessageReceived callback will always handle the message.

Add a new file called MyFirebaseMessagingService.cs and replace its template code with the following:

using System;
using Android.App;
using Android.Content;
using Android.Media;
using Android.Util;
using Firebase.Messaging;
using MyProj; 
using System.Collections.Generic;
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
   // private string TAG = "MyFirebaseMsgService";
      public override void OnMessageReceived(RemoteMessage message)
   {
       base.OnMessageReceived(message);
       SendNotification(  message.GetNotification().Body, message.Data);
   }
private void SendNotification(string messageBody, IDictionary<string, string> data)
 {  
var intent = new Intent(this, typeof(MainActivity));     intent.AddFlags(ActivityFlags.ClearTop);    
 foreach (var key in data.Keys)    
 {        
   intent.PutExtra(key, data[key]);    
 }
var pendingIntent = PendingIntent.GetActivity(this,                                                   MainActivity.NOTIFICATION_ID,                                                   intent,                                                   PendingIntentFlags.OneShot);
var notificationBuilder = new  NotificationCompat.Builder(this, MainActivity.CHANNEL_ID)                               .SetSmallIcon(Resource.Drawable.ic_stat_ic_notification)                               .SetContentTitle("FCM Message")                               .SetContentText(messageBody)                               .SetAutoCancel(true)                               .SetContentIntent(pendingIntent);
var notificationManager = NotificationManagerCompat.From(this);     notificationManager.Notify(MainActivity.NOTIFICATION_ID, notificationBuilder.Build()); 
}
}

The SendNotification method uses NotificationCompat.Builder to create the notification, and NotificationManagerCompat is used to launch the notification. The notification holds a PendingIntent that will allow the user to open the app and view the contents of the string passed into messageBody. For more information about NotificationCompat.Builder, chec out the documentation for local notifications.

Note that the MESSAGING_EVENT intent filter must be declared so that new FCM messages are directed to MyFirebaseMessagingService:

When the client app receives a message from FCM, OnMessageReceived extracts the message content from the passed-in RemoteMessage object by calling its GetNotification method.

Topic-based Messages:

The code written thus far handles registration tokens and adds remote notification functionality to the app. The next example adds code that listens for topic messages and forwards them to the user as remote notifications. Topic messages are FCM messages that are sent to one or more devices that subscribe to a particular topic. For more information about topic messages, see the documentation on topic messaging.

Subscribe to a topic

You can use the below code to register to a Topic:

Where “news” is the string name of the topic, This code locates the Subscribe to Notification button in the layout and assigns its click handler to code that calls FirebaseMessaging.Instance.SubscribeToTopic, passing in the subscribed topic, news. When the user taps the Subscribe button, the app subscribes to the news topic. In the following section, a news topic message will be sent from the Firebase Console Notifications GUI.

Use the following steps to send a topic message:

  1. In the Firebase Console, click NEW MESSAGE.
  2. On the Compose message page, enter the message text and select Topic.
  3. In the Topic pull-down menu, select the built-in topic, news:

4. On the Android device (or emulator), background the app by tapping the Android Overview button and touching the home screen.

5. When the device is ready, click SEND MESSAGE in the Firebase Console. Check the IDE output window to see /topics/news in the log output:

When this message is seen in the output window, the notification icon should also appear in the notification area on the Android device. Open the notification icon to view the topic message:

If you don’t receive a message, try deleting your app on the device (or emulator) and repeat the above steps.

Troubleshooting issues

The following describes issues and workarounds that may arise when using Firebase Cloud Messaging with Xamarin.Android.

FirebaseApp is not Initialized

In some cases, you may see this error message:

This is a known problem that you can work around by cleaning the solution and rebuilding the project (Build > Clean Solution, Build > Rebuild Solution).

Firebase package is not installed

In some cases, you may see this error message when you have both Google Maps and Firebase Cloud Messaging :

You can check out this question on StackOverflow, which deals with and solves this issue.

Summary:

This walkthrough detailed the steps for implementing Firebase Cloud Messaging remote notifications in a Xamarin.Android application. It described how to install the required packages needed for FCM communications and how to configure the Android Manifest for access to FCM servers.

It also provided example code that illustrates how to check for the presence of Google Play Services, how to implement an instance ID listener service that negotiates with FCM for a registration token, and it explained how this code creates notifications while the app is backgrounded.

Note: Certain contents in this blog are picked up from the Microsoft Docs.

We also looked at how to subscribe to topic messages and worked through an example implementation of a message listener service that is used to receive and display remote notifications while the app is running in the foreground.

In case you think I’ve missed something, go ahead and add it in the comments— I will make sure I add any other content in the post. Also, if you find something incorrect in the blog please go ahead and correct me in the comments.

You can reach out to me on LinkedIn or StackOverflow! I am always available! 😛

Fritz

Our team has been at the forefront of Artificial Intelligence and Machine Learning research for more than 15 years and we're using our collective intelligence to help others learn, understand and grow using these new technologies in ethical and sustainable ways.

Comments 0 Responses

Leave a Reply

Your email address will not be published. Required fields are marked *

wix banner square