Remote Notifications with Firebase Cloud Messaging

PDF for offline use
Related Articles:
Related Samples:

Let us know how you feel about this


0/250

last updated: 2017-02

This walkthrough provides a step-by-step explanation of how to use Firebase Cloud Messaging to implement remote notifications (also called push notifications) in a Xamarin.Android application. It illustrates how to implement the various classes that are needed for communications with Firebase Cloud Messaging (FCM), provides examples of how to configure the Android Manifest for access to FCM, and demonstrates downstream messaging using the Firebase Console.

Overview

In this walkthrough, a basic app called FCMClient will be created to illustrate the essentials of FCM messaging. FCMClient checks for the presence of Google Play Services, receives registration tokens from FCM, displays remote notifications that you send from the Firebase Console, and subscribes to topic messages:

The following topic areas will be explored:

  1. Background Notifications

  2. Topic Messages

  3. Foreground Notifications

During this walkthrough, you will incrementally add functionality to FCMClient and run it on a device or emulator to understand how it interacts with FCM. You will use logging to witness live app transactions with FCM servers, and you will observe how notifications are generated from FCM messages that you enter into the Firebase Console Notifications GUI.

Requirements

Before you can proceed with this walkthrough, you must acquire the necessary credentials to use Google's FCM servers; this process is explained in Firebase Cloud Messaging. In particular, you must download the google-services.json file to use with the example code presented in this walkthrough. If you have not yet created a project in the Firebase Console (or if you have not yet downloaded the google-services.json file), see Firebase Cloud Messaging.

To run the example app, you will need an Android test device or emulator that is compatibile with Firebase. Firebase Cloud Messaging supports clients running on Android 2.3 (Gingerbread) or higher, and these devices must also have the the Google Play Store app installed (Google Play Services 9.2.1 or later is required). If you do not yet have the Google Play Store app installed on your device, visit the Google Play web site to download and install it. Alternately, you can use the Android SDK emulator running Android 2.3 or later instead of a test device (you do not have to install the Google Play Store if you are using the Android SDK emulator).

Start an App Project

To begin, create a new empty Xamarin.Android project called FCMClient. If you are not familiar with creating Xamarin.Android projects, see Hello, Android. After the new app is created, the next step is to set the package name and install several NuGet packages that will be used for communication with FCM.

Set the Package Name

In Firebase Cloud Messaging, you specified a package name for the FCM-enabled app. This package name also serves as the application ID that is associated with the API key. Configure the app to use this package name:

  1. Open the properties for the FCMClient project.

  2. In the Android Manifest page, set the package name.

In the following example, the package name is set to com.xamarin.fcmexample:

  1. Open the properties for the FCMClient project.

  2. In the Android Application page, set the package name.

In the following example, the package name is set to com.xamarin.fcmexample:

Note that the client app will be unable to receive a registration token from FCM if this package name does not exactly match the package name that was entered into the Firebase Console.

Add the Xamarin Google Play Services Base Package

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

  1. In Visual Studio, right-click References > Manage NuGet Packages ....

  2. Click the Browse tab and search for Xamarin.GooglePlayServices.Base.

  3. Install this package into the FCMClient project:

  1. In Xamarin Studio, right-click Packages > Add Packages....

  2. Search for Xamarin.GooglePlayServices.Base.

  3. Install this package into the FCMClient project:

If you get an error during installation of the NuGet, close the FCMClient project, open it again, and retry the NuGet installation.

When you install Xamarin.GooglePlayServices.Base, all of the necessary dependencies are also installed. Edit MainActivity.cs and add the following using statement:

using Android.Gms.Common;

This statement makes the GoogleApiAvailability class in Xamarin.GooglePlayServices.Base available to FCMClient code. GoogleApiAvailability is used to check for the presence of Google Play Services.

Add the Xamarin Firebase Messaging Package

To receive messages from FCM, the Xamarin Firebase - Messaging package must be added to the app project. Without this package, an Android application cannot receive messages from FCM servers.

  1. In Visual Studio, right-click References > Manage NuGet Packages ....

  2. Check Include prerelease and search for Xamarin.Firebase.Messaging.

  3. Install this package into the FCMClient project:

  1. In Xamarin Studio, right-click Packages > Add Packages....

  2. Check Show pre-release packages and search for Xamarin.Firebase.Messaging.

  3. Install this package into the FCMClient project:

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;

The first two statements make types in the Xamarin.Firebase.Messaging NuGet package available to FCMClient code. Android.Util adds logging functionality that will be used to observe transactions with FMS.

Add the Google Services JSON File

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

  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 pane, set the Build Action to GoogleServicesJson (if the GoogleServicesJson build action is not shown, save and close the Solution, then reopen it):

  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:

When google-services.json is added to the project (and the GoogleServicesJson build action is set), the build process extracts the client ID and API key and then adds these credentials to the merged/generated AndroidManifest.xml that resides at obj/Debug/android/AndroidManifest.xml. This merge process automatically adds any permissions and other FCM elements that are needed for connection to FCM servers.

Check for Google Play Services

An initial layout for the app's UI will be created first. Edit Resources/layout/Main.axml and replace its contents with the following XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">
    <TextView
        android:text=" "
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/msgText"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:padding="10dp" />
</LinearLayout>

This TextView will be used to display messages that indicate whether Google Play Services is installed. Save the changes to Main.axml. Edit MainActivity.cs and add the following instance variable declaration to the MainActivity class:

TextView msgText;

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). 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
        {
            msgText.Text = "This device is not supported";
            Finish ();
        }
        return false;
    }
    else
    {
        msgText.Text = "Google Play Services is available.";
        return true;
    }
}

This code checks the device to see if the Google Play Services APK is installed. If it is not installed, a message is displayed in the TextBox that instructs the user to download an APK from the Google Play Store (or to enable it in the device's system settings).

Replace the OnCreate method with the following code:

protected override void OnCreate (Bundle bundle)
{
    base.OnCreate (bundle);
    SetContentView (Resource.Layout.Main);
    msgText = FindViewById<TextView> (Resource.Id.msgText);
    IsPlayServicesAvailable ();
}

IsPlayServicesAvailable is called at the end of OnCreate so that the Google Play Services check runs each time the app starts. If your app has an OnResume method, it should call IsPlayServicesAvailable from OnResume as well. Completely rebuild and run the app. If all is configured properly, you should see a screen that looks like the following screenshot:

If you don't get this result, verify that the Google Play Services APK is installed on your device (for more information, see Setting Up Google Play Services). Also verify that you have added the Xamarin.Google.Play.Services.Base package to your FCMClient project as explained earlier.

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. (For more about registration tokens, see Registration with FCM.) The FirebaseInstanceIdService service is required for FCM to be able to send messages to the device. When the FirebaseInstanceIdService service is added to the client app, the app will automatically receive FCM messages and display them as notifications whenever the app is backgrounded.

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 implementation that provides a unique identifier for each app instance. This receiver also authenticates and authorizes actions.

  • Declares an internal FirebaseInstanceIdInternalReceiver implementation that is used to start services securely.

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

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 FCMExample
{
    [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 an OnTokenRefresh 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 is 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).

OnTokenRefresh also calls SendRegistrationToAppServer to associate the user's registration token with the server-side account (if any) that is maintained by the application:

void SendRegistrationToAppServer (string token)
{
    // Add custom implementation here as needed.
}

Because this implementation depends on the design of the app server, an empty method body is provided in this example. If your app server requires FCM registration information, modify SendRegistrationToAppServer to associate the user's FCM instance ID token with any server-side account maintained by your app. (Note that the token is opaque to the client app.)

When a token is sent to the app server, SendRegistrationToAppServer should maintain a boolean to indicate whether the token has been sent to the server. If this boolean is false, SendRegistrationToAppServer sends the token to the app server – otherwise, the token was already sent to the app server in a previous call. In some cases (such as this FCMClient example), the app server does not need the token; therefore, this method is not required for this example.

Implement Client App Code

Now that the receiver services are in place, client app code can be written to take advantage of these services. In the following sections, a button is added to the UI to log the registration token (also called the Instance ID token), and more code is added to MainActivity to view Intent information when the app is launched from a notification:

Log Tokens

The code added in this step is intended only for demonstration purposes – a production client app would have no need to log registration tokens. Edit Resources/layout/Main.axml and add the following Button declaration immediately after the TextView element:

<Button
  android:id="@+id/logTokenButton"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="center_horizontal"
  android:text="Log Token" />

Edit MainActivity.cs and add the following member declaration to the MainActivity class:

const string TAG = "MainActivity";

Add the following code to the end of the OnCreate method:

var logTokenButton = FindViewById<Button>(Resource.Id.logTokenButton);
logTokenButton.Click += delegate {
    Log.Debug(TAG, "InstanceID token: " + FirebaseInstanceId.Instance.Token);
};

This code logs the current token to the output window when the Log Token button is tapped.

Handle Notification Intents

When the user taps a notification issued from FCMClient, any data accompanying that notification message is made available in Intent extras. Edit MainActivity.cs and add the following code to the top of the OnCreate method (before the call to IsPlayServicesAvailable):

if (Intent.Extras != null)
{
    foreach (var key in Intent.Extras.KeySet())
    {
        var value = Intent.Extras.GetString(key);
        Log.Debug(TAG, "Key: {0} Value: {1}", key, value);
    }
}

The app's launcher Intent is fired when the user taps its notification message, so this code will log any accompanying data in the Intent to the output window. If a different Intent must be fired, the click_action field of the notification message must be set to that Intent (the launcher Intent is used when no click_action is specified).

Background Notifications

Build and run the FCMClient app. The Log Token button is displayed:

Tap the Log Token button. A message like the following should be displayed in the IDE output window:

The long string labeled with InstanceID token is the instance ID token that you will paste into the Firebase Console – select and copy this string to the clipboard. If you do not see an instance ID token, add the following line to the top of the OnCreate method to verify that google-services.json was parsed correctly:

Log.Debug(TAG, "google app id: " + Resource.String.google_app_id);

The google_app_id value logged to the output window should match the mobilesdk_app_id value recorded in google-services.json.

Send a Message

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 of the Firebase Console:

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:

Tap the notification icon to return to the FCMClient app. The Intent extras sent to FCMClient are listed in the IDE output window:

In this example, the from key is set to the Firebase project number of the app (in this example, 41590732), and the collapse_key is set to its package name (com.xamarin.fcmexample). If you do not receive a message, try deleting the FCMClient app on the device (or emulator) and repeat the above steps.

Add a Custom Default Notification Icon

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/icstatic_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).

Handle Topic 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 Topic Messaging.

Subscribe to a Topic

Edit Resources/layout/Main.axml and add the following Button declaration immediately after the previous Button element:

<Button
  android:id="@+id/subscribeButton"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="center_horizontal"
  android:layout_marginTop="20dp"
  android:text="Subscribe to Notifications" />

This XML adds a Subscribe to Notification button to the layout. Edit MainActivity.cs and add the following code to the end of the OnCreate method:

var subscribeButton = FindViewById<Button>(Resource.Id.subscribeButton);
subscribeButton.Click += delegate {
    FirebaseMessaging.Instance.SubscribeToTopic("news");
    Log.Debug(TAG, "Subscribed to remote notifications");
};

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.

Send a Topic Message

Rebuild the app and run it again. Click the Subscribe to Notifications button:

If the app has subscribed successfully, you should see topic sync succeeded in the IDE output window:

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.

  6. Check the IDE output window to see the incoming topic message:

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 do not receive a message, try deleting the FCMClient app on the device (or emulator) and repeat the above steps.

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 is running in the foreground.

Implement FirebaseMessagingService

The FirebaseMessagingService service includes an OnMessageReceived method that you write to handle incoming messages. Note that OnMessageReceived is invoked for notification messages only when the app is running in the foreground. When the app is running in the background, an automatically generated notification is displayed (as demonstrated earlier in this walkthrough).

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;

namespace FCMExample
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    public class MyFirebaseMessagingService : FirebaseMessagingService
    {
        const string TAG = "MyFirebaseMsgService";
        public override void OnMessageReceived(RemoteMessage message)
        {
            Log.Debug(TAG, "From: " + message.From);
            Log.Debug(TAG, "Notification Message Body: " + message.GetNotification().Body);
        }
    }
}

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

[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]

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. Next, it logs the message content so that it can be viewed in the IDE output window:

Log.Debug(TAG, "From: " + message.From);
Log.Debug(TAG, "Notification Message Body: " + message.GetNotification().Body);

Send Another Message

Rebuild the app, run it again, and follow these steps to send another message:

  1. In the Firebase Console, click NEW MESSAGE.

  2. On the Compose message page, enter the message text and select Single device.

  3. Copy the token string from the IDE output window and paste it into the FCM registration token field of the Firebase Console as before.

  4. Ensure that the app is running in the foreground, then click SEND MESSAGE in the Firebase Console:

  5. When the Review message dialog is displayed, click SEND.

  6. The incoming message is logged to the IDE output window:

Add a Local Notifications Sender

In this remaining example, the incoming FCM message will be converted into a local notification that is launched while the app is running in the foreground. Edit MyFirebaseMessageService.cs and add the following using statement:

using FCMClient;

Add the following method to MyFirebaseMessagingService:

void SendNotification(string messageBody)
{
    var intent = new Intent(this, typeof(MainActivity));
    intent.AddFlags(ActivityFlags.ClearTop);
    var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);

    var notificationBuilder = new Notification.Builder(this)
        .SetSmallIcon(Resource.Drawable.ic_stat_ic_notification)
        .SetContentTitle("FCM Message")
        .SetContentText(messageBody)
        .SetAutoCancel(true)
        .SetContentIntent(pendingIntent);

    var notificationManager = NotificationManager.FromContext(this);
    notificationManager.Notify(0, notificationBuilder.Build());
}

To distinguish this notification from background notifications, this code marks notifications with an icon that differs from the applicatiion icon. Add ic_stat_ic_notification.png to Resources/drawable and include it in the FCMClient project.

The SendNotification method uses Notification.Builder to create the notification, and NotificationManager 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. Depending on the versions of Android that you intend to target with your app, you may want to use NotificationCompat.Builder instead. For more information about Notification.Builder, see Local Notifications.

Call the SendNotification method at end of the OnMessageReceived method:

SendNotification (message.GetNotification().Body);

As a result of these changes, SendNotification will run whenever a notification is received while the app is in the foreground, and the notification will appear in the notification area. If the app is backgrounded, SendNotification will not run and, instead, a background notification (illustrated earlier in this walkthrough) will be launched.

Send the Last Message

Rebuild the app, run it again, then use the following steps to send the last message:

  1. In the Firebase Console, click NEW MESSAGE.

  2. On the Compose message page, enter the message text and select Single device.

  3. Copy the token string from the IDE output window and paste it into the FCM registration token field of the Firebase Console as before.

  4. Ensure that the app is running in the foreground, then click SEND MESSAGE in the Firebase Console:

This time, the message that was logged in the output window is also packaged in a new notification – the notification icon appears in the notification tray while the app is running in the foreground:

When you open the notification, you should see the last message that was sent from the Firebase Console Notifications GUI:

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 it explained how to configure the Android Manifest for access to FCM servers. It provided example code that illustrates how to check for the presence of Google Play Services. It demonstrated how to implement an instance ID listener service that negotiates with FCM for a registration token, and it explained how this code creates background notifications while the app is backgrounded. It explained how to subscribe to topic messages, and it provided 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.

Xamarin Workbook

If it's not already installed, install the Xamarin Workbooks app first. The workbook file should download automatically, but if it doesn't, just click to start the workbook download manually.