dotnetco.de

Setup Xamarin Forms Android App for Push Notifications

Now that we have setup Microsoft Azure, Google Firebase Cloud Messaging and also our new Azure Mobile App, it’s time to update our existing Xamarin Forms App. First we start with the Android part as this is the easy one because it works on both Windows and Mac and push notifications also work fine in the Android Emulator (in opposite to Apples iPhone Simulator because for testing push notifications on iPhone you need iPhone hardware).

This posting is part of a series of 3 postings regarding push notifications with Xamarin Forms:

  1. Configure Azure and Google Firebase for push notifications in Xamarin Forms
  2. Setup server part for Xamarin Forms Push Notifications
  3. Setup Xamarin Forms Android App for Push Notifications (the current posting)

Setup Android part in Xamarin Forms App

A good explanation from Microsoft could be found in their Azure docs, but it’s tailored for their ToDoItemManager Demo so we have to do some more work to implement it to our existing app from scratch. Here are the steps to be done. And don’t worry if you get errors while you are adding all the code. They will disappear as soon as you have completed all the steps.

  1. In your PCL project, add a new class “PushClient.cs”. (Microsofts demo has it in the TodoItemManager.cs but as we want to add the push notification to our own app we have to create a new class in the PCL for this code). Remember your namespace and replace the whole file with the following code:
    using System;
    using Microsoft.WindowsAzure.MobileServices;
    
    namespace MyProject
    {
      public class PushClient
      {
        static PushClient defaultInstance = new PushClient();
        MobileServiceClient client;
        private const string ApplicationURL = "https://myproject.azurewebsites.net";
    
        public PushClient()
        {
          this.client = new MobileServiceClient(ApplicationURL);
        }
    
        public static PushClient DefaultManager
        {
          get 
          {
            return defaultInstance;
          }
          private set 
          {
            defaultInstance = value;
          }
        }
        public MobileServiceClient CurrentClient
        {
          get { return client; }
        }
      }
    }
  2. Afterwards do the following changes in your PushClient.cs:
    1. Replace your namespace in line 4.
    2. In line 10, set your correct application url from Azure. If you forgot: You will find it in your Azure Portal at “App Services” -> Your Push Notification App -> “Overview” at “URL” at the right. Take care about the protocol: I have no idea why Azure shows “http://YourURL.azurewebsites.net” because Microsoft has a wildcard SSL certificate for *.azurewebsites,net for everyone for free (see here). So I would always advise to use https instead of http for every transfer so check whether your URL works fine with https and then use this protocol.
  3. Now go to your Droid project, right-click on “Components” and select “Get more components”. In section “Cloud Services” you’ll find “Google Cloud Messaging Client“. Don’t be afraid about the name (GCM, not FCM) nor the old version (current release 1.0 is from December 2013), it still works fine also with Firebase. So add it to your Droid Project.
  4. Open MainActivity.cs and add “using Gcm.Client;” to the top.
  5. Within “OnCreate” method, add this piece of code right after the line “LoadApplication”:
    try
     {
         // Check to ensure everything's set up right
         GcmClient.CheckDevice(this);
         GcmClient.CheckManifest(this);
    
         // Register for push notifications
         System.Diagnostics.Debug.WriteLine("Registering...");
         GcmClient.Register(this, PushHandlerBroadcastReceiver.SENDER_IDS);
     }
     catch (Java.Net.MalformedURLException)
     {
         CreateAndShowDialog("There was an error creating the client. Verify the URL.", "Error");
     }
     catch (Exception e)
     {
         CreateAndShowDialog(e.Message, "Error");
     }
  6. In above code, a new method “CreateAndShowDialog” has been used so we need to add it to the MainActivity.cs. Additionally we have to create a new MainActivity instance for execution in main UI thread. Personally I like to structure the code a bit so it’s easier months or years later to find out why some code has been added. So here is the code to be added to the MainActivity.cs encapsulated in a region.
    #region " Added for Push Notifications"
    private void CreateAndShowDialog(String message, String title)
    {
      AlertDialog.Builder builder = new AlertDialog.Builder(this);
    
      builder.SetMessage(message);
      builder.SetTitle(title);
      builder.Create().Show();
    }
    
    // Create a new instance field for this activity.
    static MainActivity instance = null;
    
    // Return the current activity instance.
    public static MainActivity CurrentActivity
    {
      get
      {
        return instance;
      }
    }
    #endregion
  7. Finally we also have to set the new MainActivity instance to current one. Add it to the beginning of “OnCreate” in MainActivity.cs.
    // Set the current instance of MainActivity.
     instance = this;
  8. Next we need to create a new class. So right-click on your Droid Project, select “Add” -> “New File”, select “General” -> “Empty Class” and choose name “GcmService.cs”.
  9. Now we need to add several code blocks. In the end, we replace the empty class completely. So just note down your namespace (“MyProject.Droid”), clear all code from new GcmService.cs and insert the following code. Afterwards, replace “MyProject.Droid” with your own namespace!
    using Android.App;
    using Android.Content;
    using Android.Media;
    using Android.Support.V4.App;
    using Android.Util;
    using Gcm.Client;
    using Microsoft.WindowsAzure.MobileServices;
    using Newtonsoft.Json.Linq;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Text;
    
    [assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
    [assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
    [assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
    [assembly: UsesPermission(Name = "android.permission.INTERNET")]
    [assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
    //GET_ACCOUNTS is only needed for android versions 4.0.3 and below
    [assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]
    
    namespace MyProject.Droid
    {
      [BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
      [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })]
      [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })]
      [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })]
      public class PushHandlerBroadcastReceiver : GcmBroadcastReceiverBase<GcmService>
      {
        public static string[] SENDER_IDS = new string[] { "<PROJECT_NUMBER>" };
      }
    
      [Service]
      public class GcmService : GcmServiceBase
      {
        public static string RegistrationID { get; private set; }
    
        public GcmService() : base(PushHandlerBroadcastReceiver.SENDER_IDS) { }
    
    
        protected override void OnRegistered(Context context, string registrationId)
        {
          Log.Verbose("PushHandlerBroadcastReceiver", "GCM Registered: " + registrationId);
          RegistrationID = registrationId;
    
          var push = PushClient.DefaultManager.CurrentClient.GetPush();
    
          MainActivity.CurrentActivity.RunOnUiThread(() => Register(push, null));
        }
    
        public async void Register(Microsoft.WindowsAzure.MobileServices.Push push, IEnumerable<string> tags)
        {
          try
          {
            const string templateBodyGCM = "{\"data\":{\"message\":\"$(messageParam)\"}}";
    
            JObject templates = new JObject();
            templates["genericMessage"] = new JObject
          {
            {"body", templateBodyGCM}
          };
    
            await push.RegisterAsync(RegistrationID, templates);
            Log.Info("Push Installation Id", push.InstallationId.ToString());
          }
          catch (Exception ex)
          {
            System.Diagnostics.Debug.WriteLine(ex.Message);
            Debugger.Break();
          }
        }
    
        protected override void OnMessage(Context context, Intent intent)
        {
          Log.Info("PushHandlerBroadcastReceiver", "GCM Message Received!");
    
          var msg = new StringBuilder();
    
          if (intent != null && intent.Extras != null)
          {
            foreach (var key in intent.Extras.KeySet())
              msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
          }
    
          //Store the message
          var prefs = GetSharedPreferences(context.PackageName, FileCreationMode.Private);
          var edit = prefs.Edit();
          edit.PutString("last_msg", msg.ToString());
          edit.Commit();
    
          string message = intent.Extras.GetString("message");
          if (!string.IsNullOrEmpty(message))
          {
            createNotification("New todo item!", "Todo item: " + message);
            return;
          }
    
          string msg2 = intent.Extras.GetString("msg");
          if (!string.IsNullOrEmpty(msg2))
          {
            createNotification("New hub message!", msg2);
            return;
          }
    
          createNotification("Unknown message details", msg.ToString());
        }
    
        void createNotification(string title, string desc)
        {
          //Create notification
          var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
    
          //Create an intent to show ui
          var uiIntent = new Intent(this, typeof(MainActivity));
    
          //Use Notification Builder
          NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    
          //Create the notification
          //we use the pending intent, passing our ui intent over which will get called
          //when the notification is tapped.
          var notification = builder.SetContentIntent(PendingIntent.GetActivity(this, 0, uiIntent, 0))
              .SetSmallIcon(Android.Resource.Drawable.SymActionEmail)
              .SetTicker(title)
              .SetContentTitle(title)
              .SetContentText(desc)
    
              //Set the notification sound
              .SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
    
              //Auto cancel will remove the notification once the user touches it
              .SetAutoCancel(true).Build();
    
          //Show the notification
          notificationManager.Notify(1, notification);
        }
    
    
        protected override void OnUnRegistered(Context context, string registrationId)
        {
          Log.Error("PushHandlerBroadcastReceiver", "Unregistered RegisterationId : " + registrationId);
        }
    
        protected override void OnError(Context context, string errorId)
        {
          Log.Error("PushHandlerBroadcastReceiver", "GCM Error: " + errorId);
        }
    
      }
    
    }

     

  10. Some modifications have been done to the above listed code:
    1. As said, replace namespace “MyProject.Droid” in line 22 with your own namespace.
    2. In line 30, replace “<PROJECT_NUMBER>” with the Project Number from your Google Firebase Cloud Messaging Project. You will find it on “Project Settings” -> “Cloud Computing”, it’s named “Sender ID”. It’s a number with 13 digits.
  11. Now clean your solution and rebuild it. It should compile fine.

Leave a Comment