Push Notifications in Xamarin.Android

Overview

In this article you will see the way you can create a basic Android app (in Xamarin.Android) that uses Push Notifications.

As a general idea, a notification is a message delivered by a backend to your device in (almost) real – time. This is one of the most effective ways of improving the interactivity of your mobile app, because the user knows in a very short amount of time that a specific event has occurred.

Every mobile OS has an associated Notification Server, which is a system that mediates the communication between your backend (or app server) and the client app (device). It includes a lot of important functionalities, like: message queuing and routing, partitioning messages on topics, reliability, and so on.

The associated Notification Server for Android is well known as Google Cloud Messaging, and now it’s part of Firebase infrastructure. The actual picture of the entire process is the following:

In order to receive notifications, our app needs to register to the GCM (it’s more like a subscription), so that the GCM should know to uniquely identify the client app.

As we’ll see in a further section of this article, GCM is a entire service. As users, we will simply create an application inside this service and enable the Cloud Messaging feature. When we’ll create our application, we’ll receive 2 important ID’s: API_KEY and Sender ID. In order to register to the GCM, we need to send the Sender ID to it’s API and then we’ll receive a Registration Token that we’ll use to subscribe to one of it’s topics.

The registration flow is the following:

The last step (sending the registration token to your app server) is very important if you want to manage the connections of your devices or if you want to see some statistics regarding the number of requests performed.

For other Notification Servers, those flows may be similar but it is recommended to study carefully their documentation to see exactly what steps you need to perform.

The most known notification servers are:

  • 1. Apple Push Notification Service
  • 2. Google Cloud Messaging
  • 3. Windows Notification Service (for UWP)
  • 4. Microsoft Push Notification Service (for Windows Phone)
  • 5. Amazon Device Messaging
  • 6. Baidu (for Android China)

Creating the client app

1. Installing the GCM package

Let’s create the Xamarin.Android application that we’ll use to receive notifications: Open Visual Studio and create a new Android Blank App.

First, we need to make install the GCM package for Xamarin. In order to do that, go to Tools – Nuget Package Manager – Manage Nuget Packages For Solution. And search for: xamarin google play gcm. You need to select the first package:

Select the project that you want to install the package in, and click on “Install”. If it gives you an error when trying to install it, then choose an older version (I installed the 29.0.0.2 version).

Now add those 2 include statements in the MainActivity.cs: 

 using Android.Gms.Common;
 using Android.Util;

2. Setting the package name

Now we should set the package name of our application. Go to Project – Properties – Android Manifest and set the package name of your app. We’ll need this package name when we’ll set our GCM instance:

3. Setting the permissions in android manifest

We need to set the permissions for our application. “Permissions” refer to the capabilities that our app needs in order to run properly (for example: internet, sms, etc.). When the user installs the app (from the Play Store) it will be informed that the app that he’s going to install will require those permissions. It’s better to add only the permissions that your app really needs.

Open the manifest file (it’s located in <the directory of your project> / Properties / AndroidManifest.xml. You can open it with your favourite text editor and add the following permissions:

 <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
 <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
 <uses-permission android:name="android.permission.WAKE_LOCK" />
 <uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="com.xamarin.client_app.permission.C2D_MESSAGE" />
 <permission android:name="com.xamarin.client_app.permission.C2D_MESSAGE" android:protectionLevel="signature" />
 <application android:label="Client" android:icon="@drawable/Icon"></application>

All those permissions will be placed inside the existent <manifest> tag.

Save the file and remember it’s location (we’ll add the GCM setup in a further section of this article).

4. Updating the UI

For this simple application, we won’t need any fancy UI, just a simple Text View where we can put some status message. Update the Resources/layout/Main.axml as follows:

<?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/textView"
 android:textAppearance="?android:attr/textAppearanceMedium"
 android:padding="10dp" />
</LinearLayout>

As you can see, we just create a text view and place it on the screen. It’s id is textView.

5. Checking for Google Play Services

In order to communicate with the Google Cloud Messaging, we’ll need the communication infrastructure maintained by the Google Play Services application. GCP technically has a background thread that listens for incoming messages from GCM and forwards them to the application that has a corresponding handler to process the message.

In order to check for GCP, add the following method to the MainActivity.cs file:

private bool IsPlayServicesAvailable()
{
    int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);

    if (resultCode != ConnectionResult.Success)
    {
       if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
         textView.Text = GoogleApiAvailability.Instance.GetErrorString(resultCode);
       else
       {
         textView.Text = "Sorry, this device is not supported";
         Finish();
       }
       return false;
    }
    else
    {
       textView.Text = "Google Play Services is available.";
       return true;
    }
}

As you can see, this method checks for the existence of the GCP application and displays a message on the screen (in the associated TextView that we created before).

Update the OnCreate method with the following contents:

protected override void OnCreate(Bundle bundle)
{
   base.OnCreate(bundle);

   // Set our view from the "main" layout resource
   SetContentView (Resource.Layout.Main);

   textView = FindViewById<TextView>(Resource.Id.textView);

   IsPlayServicesAvailable();
}

Just run the app (we recommend to run the app on a real device and you’ll have to see the following message):

6. Registering with the Google Cloud Messaging

Add a new class to the project, called RegistrationService.cs and add the following code into it:

[Service(Exported = false)]
 class RegistrationService : IntentService
 {
    public RegistrationService() : base("RegistrationIntentService") { }

    protected override void OnHandleIntent(Intent intent)
    {
      try
      {
         Log.Info("RegistrationIntentService", "Calling InstanceID.GetToken");
 
         var instanceID = InstanceID.GetInstance(this);
         var token = instanceID.GetToken(
         "YOUR_SENDER_ID", GoogleCloudMessaging.InstanceIdScope, null);

         Log.Info("RegistrationIntentService", "GCM Registration Token: " + token);

         Subscribe(token);
 
      }
      catch (Exception e)
      {
         Log.Debug("RegistrationIntentService", "Failed to get a registration token");
         return;
      }
    }

    void Subscribe(string token)
    {
       var pubSub = GcmPubSub.GetInstance(this);
       pubSub.Subscribe(token, "/topics/global", null);
    }
}

As you can see, this class is an IntentService, which is kind of a Background Service (in Android) used to send / receive requests. It implements the work queue processor pattern.

The YOUR_SENDER_ID will have to replaced with the sender ID of the GCM instance that we’ll create, as you’ll see in the next section.

Also we have a Subscribe method that we use to hook our app to the /topics/global topic. Please notice that we need the token to subscribe, so the registration step is mandatory.

Creating the Google Cloud Messaging Instance

Go to that link, log in and create an app. Choose a name, insert the exact package name that you set in your application, select your country and click on Continue..

Select Cloud Messaging and click on Enable:

You will receive the Sender Id and the API KEY:

Note: those credentials are only for demonstration purposes. They may not be available at the time you’re reading this article.

Checking the registration step

Now that we have the Sender ID, go in the RegistrationService class and replace YOUR_SENDER_ID with the given one and update the OnCreate method to use the Registration Service intent:

if (IsPlayServicesAvailable ())
{
     var intent = new Intent (this, typeof (RegistrationService));
     StartService (intent);
}

Now start the application, go to the Output window and you’ll see something like that:

Basically we’re watching the logs in this window and we can see that the GCM gave us a token that we can use to subscribe! If you don’t see the token, make sure that the permissions are set as specified above.

Adding the Google Cloud Messaging Listener Service

There is one last step that we need to perform in our Client App in order to be ready to receive notifications: we need to add the Listener Service that implements the logic for receiving and dispatching the notifications.

In order to do that, let’s add the GCMListenerService class to our project:

[Service(Exported = false), IntentFilter(new[] { "com.google.android.c2dm.intent.RECEIVE" })]
 public class MyGcmListenerService : GcmListenerService
 {
    public override void OnMessageReceived(string from, Bundle data)
    {
      var message = data.GetString("message");
      SendNotification(message);
    }

    void SendNotification(string message)
    {
      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.Icon)
          .SetContentTitle("GCM Message")
          .SetContentText(message)
          .SetAutoCancel(true)
          .SetContentIntent(pendingIntent);

      var notificationManager = (NotificationManager)GetSystemService(Context.NotificationService);
       notificationManager.Notify(0, notificationBuilder.Build());
    }
 }

This code runs as a background service, but it’s specialized on listening to messages from GCM. When a message is arrived, the OnMessageReceived is called. The SendNotification method creates a new Notification Builder (used by Android) and sends it to the Android OS, in order to see it as any other notification.

As you can see, the GCMListenerService class seems to be decoupled from the rest of the project (it doesn’t communicate with MainActivity (i.e. it’s not referenced in that file) nor with any other file in our project.

Actually the Android OS knows to automatically search for the classes that extend the GcmListenerService class, but only if we specify it in the manifest file, by adding the following code:

 <application android:label="Client" android:icon="@drawable/Icon">
    <receiver android:name="com.google.android.gms.gcm.GcmReceiver" 
      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="com.xamarin.client_app" />
      </intent-filter>
    </receiver>
 </application>

Right now the client app is ready to receive notifications.

Creating a backend simulator to test our app

In order to test our app, we have to create a application (mockup) that connects to GCM and sends a notification. Normally, GCM should forward that notification to our mobile app.

Add a new Windows Forms project to your solution, add a textbox and a button on the form (notification message and a button to send the notification) and  add modify the Form1 class as follows:

public partial class Form1 : Form
{
    const string API_KEY = "AIzaSyDc_0JdvJBShBsynh78Ft_sSd3vGkBsrMA";

    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
       var jGcmData = new JObject();
       var jData = new JObject();

       jData.Add("message", textBox1.Text);
       jGcmData.Add("to", "/topics/global");
       jGcmData.Add("data", jData);

       var url = new Uri("https://gcm-http.googleapis.com/gcm/send");
       try
       {
          using (var client = new HttpClient())
          {
             client.DefaultRequestHeaders.Accept.Add(
                 new MediaTypeWithQualityHeaderValue("application/json"));

             client.DefaultRequestHeaders.TryAddWithoutValidation(
                "Authorization", "key=" + API_KEY);

             Task.WaitAll(client.PostAsync(url,
                 new StringContent(jGcmData.ToString(), Encoding.Default, "application/json"))
            .ContinueWith(response =>
            {
                 Console.WriteLine(response);
                 Console.WriteLine("Message sent: check the client device notification tray.");
            }));
       }
     }
     catch (Exception ex)
     {
             Console.WriteLine("Unable to send GCM message:");
             Console.Error.WriteLine(ex.StackTrace);
     }
   }
 }

Make sure that the API_KEY is the same as the one in the Google Developers Console .

Test the app!

Run the client app on your device, make sure that you received a registration token as shown above; run the backend simulator app and send a message. You should see something similar to this picture:

 

You just created your first application that uses push notifications!

References:

This article follows closely the next 2 articles:

1- https://developer.xamarin.com/guides/android/application_fundamentals/notifications/remote-notifications-with-gcm/

2 – https://developer.xamarin.com/guides/android/application_fundamentals/notifications/google-cloud-messaging/

Leave a Reply

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