How to use Push Notifications in Xamarin Forms
Asked Answered
D

6

63

I have an app using Xamarin.Forms targeting IOS, Android and WP 8.

I need push notification feature in my app.

I have seen the pushsharp demos and it seems promising. But all the code I have seen is done separately for each platform.

I would like it to be done in the Xamarin.Forms project, somewhere in the App.cs so that I don't need to repeat the code for registering device, and handle how push notifications should be processed.

Sample codes is welcome.

Edit : I implemented it based on Idot's answer. Here is the link for my answer.

Drennan answered 13/3, 2015 at 10:17 Comment(3)
This question has been addressed in the official Xamarin Forum, have a look: forums.xamarin.com/discussion/20845/…Cornu
Its purely based on azure, and I'm looking for pushsharp. Also its not about Xamarin forms but separate implementation for each platform. But thanks for finding me something to start on.Drennan
check out HOL On Azure Push notification in Xamrin: onedrive.live.com/…Acerbity
B
57

I just implemented push notification a few days ago, and I'll share my solution here (based on PushSharp)

Step by step guide:

1) In your shared project, create an Interface called IPushNotificationRegister

public interface IPushNotificationRegister
{
    void ExtractTokenAndRegister();
}

This interface is used for fetching the push token and then send it to the server. this Token is unique per device.

2) In Your shared project, you should invoke ExtractTokenAndRegister (using your favorite IOC, I called it right after login).

Android Implementation:

3) Add Receivers for listening to events received by Google GCM service:

a)

[BroadcastReceiver]
[IntentFilter(new[] { Intent.ActionBootCompleted })]
public class GCMBootReceiver : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        MyIntentService.RunIntentInService(context, intent);
        SetResult(Result.Ok, null, null);
    }
}

b)

[assembly: Permission(Name = "@[email protected]_MESSAGE")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
[assembly: UsesPermission(Name = "@[email protected]_MESSAGE")]
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]
[assembly: UsesPermission(Name = "android.permission.INTERNET")]

namespace Consumer.Mobile.Droid.PushNotification
{
    [BroadcastReceiver(Permission = "com.google.android.c2dm.permission.SEND")]
    [IntentFilter(new string[] { "com.google.android.c2dm.intent.RECEIVE" }, Categories = new string[] { "@PACKAGE_NAME@" })]
    [IntentFilter(new string[] { "com.google.android.c2dm.intent.REGISTRATION" }, Categories = new string[] { "@PACKAGE_NAME@" })]
    [IntentFilter(new string[] { "com.google.android.gcm.intent.RETRY" }, Categories = new string[] { "@PACKAGE_NAME@" })]
    [IntentFilter (new[]{ Intent.ActionBootCompleted }, Categories = new[]{ Intent.CategoryDefault })]
    public class GCMBroadcastReceiver : BroadcastReceiver
    {
        public override void OnReceive(Context context, Intent intent)
        {
            MyIntentService.RunIntentInService(context, intent);
            SetResult(Result.Ok, null, null);
        }
    }
}

c) Add Intent service to process the notification

using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Media;
using Android.OS;
using Android.Support.V4.App;
using Consumer.Mobile.Infra;
using Consumer.Mobile.Services.PushNotification;
using Java.Lang;
using XLabs.Ioc;
using TaskStackBuilder = Android.Support.V4.App.TaskStackBuilder;

namespace Consumer.Mobile.Droid.PushNotification
{
    [Service]
    public class MyIntentService : IntentService
    {
        private readonly ILogger _logger;
        private readonly IPushNotificationService _notificationService;
        private readonly IPushNotificationRegister _pushNotificationRegister;

        public MyIntentService()
        {
            _logger = Resolver.Resolve<ILogger>();
            _notificationService = Resolver.Resolve<IPushNotificationService>();
            _pushNotificationRegister = Resolver.Resolve<IPushNotificationRegister>();
        }

        static PowerManager.WakeLock _sWakeLock;
        static readonly object Lock = new object();


        public static void RunIntentInService(Context context, Intent intent)
        {
            lock (Lock)
            {
                if (_sWakeLock == null)
                {
                    // This is called from BroadcastReceiver, there is no init.
                    var pm = PowerManager.FromContext(context);
                    _sWakeLock = pm.NewWakeLock(
                    WakeLockFlags.Partial, "My WakeLock Tag");
                }
            }

            _sWakeLock.Acquire();
            intent.SetClass(context, typeof(MyIntentService));
            context.StartService(intent);
        }

        protected override void OnHandleIntent(Intent intent)
        {
            try
            {
                Context context = this.ApplicationContext;
                string action = intent.Action;

                if (action.Equals("com.google.android.c2dm.intent.REGISTRATION"))
                {
                    HandleRegistration(context, intent);
                }
                else if (action.Equals("com.google.android.c2dm.intent.RECEIVE"))
                {
                    HandleMessage(context, intent);
                }
            }
            finally
            {
                lock (Lock)
                {
                    //Sanity check for null as this is a public method
                    if (_sWakeLock != null)
                        _sWakeLock.Release();
                }
            }
        }

        private void HandleMessage(Context context, Intent intent)
        {

            Intent resultIntent = new Intent(this, typeof(MainActivity));


            TaskStackBuilder stackBuilder = TaskStackBuilder.Create(this);

            var c = Class.FromType(typeof(MainActivity));
            stackBuilder.AddParentStack(c);
            stackBuilder.AddNextIntent(resultIntent);

            string alert = intent.GetStringExtra("Alert");
            int number = intent.GetIntExtra("Badge", 0);

            var imageUrl = intent.GetStringExtra("ImageUrl");
            var title = intent.GetStringExtra("Title");

            Bitmap bitmap = GetBitmap(imageUrl);

            PendingIntent resultPendingIntent = stackBuilder.GetPendingIntent(0, (int)PendingIntentFlags.UpdateCurrent);

            NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .SetAutoCancel(true) // dismiss the notification from the notification area when the user clicks on it
                .SetContentIntent(resultPendingIntent) // start up this activity when the user clicks the intent.
                .SetContentTitle(title) // Set the title
                .SetNumber(number) // Display the count in the Content Info
                .SetSmallIcon(Resource.Drawable.Icon) // This is the icon to display
                .SetLargeIcon(bitmap)
                .SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
                .SetContentText(alert); // the message to display.

            // Build the notification:
            Notification notification = builder.Build();

            // Get the notification manager:
            NotificationManager notificationManager =
                GetSystemService(Context.NotificationService) as NotificationManager;

            // Publish the notification:
            const int notificationId = 0;
            notificationManager.Notify(notificationId, notification);
        }

        private void HandleRegistration(Context context, Intent intent)
        {
            var token = intent.GetStringExtra("registration_id");
            _logger.Info(this.Class.SimpleName, "Received Token : " + token);

            if (_pushNotificationRegister.ShouldSendToken(token))
            {
                var uid = Android.Provider.Settings.Secure.GetString(MainActivity.Context.ContentResolver, Android.Provider.Settings.Secure.AndroidId);
                _notificationService.AddPushToken(token, DeviceUtils.GetDeviceType(), uid);
            }
        }


        private Bitmap GetBitmap(string url)
        {

            try
            {
                System.Net.WebRequest request =
                    System.Net.WebRequest.Create(url);
                System.Net.WebResponse response = request.GetResponse();
                System.IO.Stream responseStream =
                    response.GetResponseStream();
                return BitmapFactory.DecodeStream(responseStream);


            }
            catch (System.Net.WebException)
            {
                return null;
            }

        }

    }
}

d) Implement the Interface IPushNotificationRegister:

using Android.App;
using Android.Content;
using Consumer.Mobile.Services;
using Consumer.Mobile.Services.PushNotification;
[assembly: Permission(Name = "@[email protected]_MESSAGE")]
[assembly: UsesPermission(Name = "@[email protected]_MESSAGE")]

// Gives the app permission to register and receive messages.
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]

// Needed to keep the processor from sleeping when a message arrives
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
[assembly: UsesPermission(Name = "android.permission.RECEIVE_BOOT_COMPLETED")]
namespace Consumer.Mobile.Droid.PushNotification
{
    public class PushNotificationRegister : IPushNotificationRegister
    {          
        public override void ExtractTokenAndRegister()
        {
            string senders = AndroidConfig.GCMSenderId;
            Intent intent = new Intent("com.google.android.c2dm.intent.REGISTER");
            intent.SetPackage("com.google.android.gsf");
            intent.PutExtra("app", PendingIntent.GetBroadcast(MainActivity.Context, 0, new Intent(), 0));
            intent.PutExtra("sender", senders);
            MainActivity.Context.StartService(intent);
        }


    }
}

iOS implementation:

4) In your AppDelegate, add the following method:

a)

public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
    var deviceTokenString = deviceToken.ToString().Replace("<","").Replace(">", "").Replace(" ", "");
    var notificationService = Resolver.Resolve<IPushNotificationService>();
    var pushNotificationRegister = Resolver.Resolve<IPushNotificationRegister>();

    if (pushNotificationRegister.ShouldSendToken(deviceTokenString))
    {
        var uid = UIDevice.CurrentDevice.IdentifierForVendor.AsString();
        notificationService.AddPushToken(deviceTokenString, DeviceUtils.GetDeviceType(), uid);
    }
}

b) Implement IPushNotificationRegister :

using Consumer.Mobile.Services;
using Consumer.Mobile.Services.PushNotification;
using UIKit;

namespace Consumer.Mobile.iOS.PushNotification
{
    public class iOSPushNotificationRegister : IPushNotificationRegister
    {
        public override void ExtractTokenAndRegister()
        {
            const UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
            UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
        }
    }
}

Regarding WP, I didn't implement it.

If you need the code on the server side using PushSharp, let me know.

You can check the client samples I've based my solution here

Burst answered 19/3, 2015 at 10:17 Comment(17)
Thank you. I will certainly have a look at it. No I wont need the server side code.Drennan
Where are you handling the DidReceiveRemoteNotification for IOS?Drennan
I didn't, if you want to add custom behavior for showing notifications while the app is running, you'll need to handle it in the AppDelegate class: public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo) {}Burst
You have broadcast receiver for android that is why i was curiousDrennan
You are right, I kept BootReceiver as I followed the guide, You can remove it if you're not interested in handling notifications while your app is running.Burst
Hi, @Burst , I am starting using PushSharp, I want to know if there is a way to get the Apple response after a remote notification is sent to a device. I have an old windows service I created a long time ago where I was able to get response (if OK or failure). But since now I have to use multi-platform notifications I need to use PushSharp if possible. Thanks.Macnamara
Yes you can! You can check the sample code here: github.com/Redth/PushSharp/blob/master/PushSharp.Sample/… where you can register to a notification 'OnNotificationSent' and others that indicate it failed.Burst
Yes, sorry I just found OnNotificationFailed 1 minute ago and was going to put here =) thank you for your help!Macnamara
@Burst I'm interested in the code server side, do you have a link or if you prefer I can open a new question regarding it.Burnight
As I stated above, I used PushSharp, if you have a question on the implementation, then please open a question and I'll happily answer, as it is not relevant for the client implementation here.Burst
@DQuaglio - See the last part of my answer, I have mentioned what I used.Drennan
@Burst your Github link for a client is not working. So could you please send me link for both client and server side using PushsharpBlunge
@pArthsavadiya - please check the reference, you can find in the main page all relevant samplesBurst
Actually I dont find IPushNotificationService interface. Where do I find that ?Millar
@AnikSaha - this is described in step #1, you need to create it.Burst
how do you implement step 2) ??Urgent
it's detailed in step 3d for Android, and step 4b for iOSBurst
D
17

I have been suggested to use the following plugin by xamarin support and forms.

This plugin works well

https://github.com/rdelrosario/xamarin-plugins/tree/master/PushNotification

Will update the answer once I get it to work.

UPDATE :

I got push notifications working for both iOS and Android.

I used Google Cloud Messaging Client, an excellent component for Android, and didn't have to write much of the code as mentioned in this answer.

My iOS implementation was similar to this, not much code required.

And for Pushing the notifications from the Server I used nuget package of PushSharp.

I didn't implement in WP, as that was not required in my project.

This Xamarin Help on Push Notifications is worth reading if you are going to implement Push Notifications.

Update (June 2018) - Use the following plugin for FCM on iOS and Android, ti supports Xamarin.Forms - FirebasePushNotificationPlugin

Drennan answered 18/3, 2015 at 16:4 Comment(13)
Is this solution working with Android 5+? For what I can see the GCM component is making them crash, is that correct?Blayne
I didn't find any issues, I will double check and get back soon. If you are having a crash please share the exact error message.Drennan
i am really struggling with Xamarin docs for same , my listener is not getting calledBrack
Thanks for this, Do this plugin support for GCM's topics so that server need not to fetch the registration_ids and client application subscribed to a topic.Iives
@SHIVANGSANGHI - you can add sender ids.Drennan
Could anyone please tell me why my push notification is being replaced when I receive a new one ?? :(Mathematics
@VishnuBabu - In android - give it a new Id, if you give the same Id it will update the ld one.Drennan
@Rohit Thank you !! I But where should I set it ??Mathematics
@VishnuBabu - notificationId in notificationManager.Notify(notificationId, notification); #29030209Drennan
using notificationManager will it work if app is not in the background ? I am trying to use it with this plug-in..Mathematics
@VishnuBabu - I dont think your question is about the existing answer or question. Please start a new thread if you have any specific question. Use the notify() method to issue the notification. When you call notify(), specify a notification ID. You can use this ID to update the notification later on. This is described in more detail in Managing Notifications. - developer.android.com/training/notify-user/…Drennan
this is now DEPRECATED sorryUrgent
@E.Rawrdríguez.Ophanim - Check the last line.Drennan
H
2

In Xamarin Forms you could also use a notifications SDK like Donky (which is the European equivalent to the American Urban Airship); you can easily make a scalable notifications project in a single day, I've twice built WhatsApp clone shells in under 35 minutes each time using this SDK. See http://docs.mobiledonky.com

Hannahannah answered 30/11, 2015 at 16:14 Comment(0)
D
1

You might look at the Appboy Component which has support for this out of the box. https://components.xamarin.com/view/appboy-sdk-bindings

As others have said, you can't do generically without some platform-specific components.

Diligence answered 21/4, 2016 at 11:46 Comment(0)
M
0

This is not possible to do in pure Xamarin.Forms but is relatively trivial to implement a solution whereby they can be handled in the App.cs (although this will require platform specific implementations).

Take a look in the IXForms implementation within the Xamarin.Forms.Labs project where the notifications are channeled back to Forms project:

https://github.com/XLabs/Xamarin-Forms-Labs

and more specifically:

https://github.com/XLabs/Xamarin-Forms-Labs/tree/master/src/Platform/XLabs.Platform/Mvvm

Meticulous answered 18/3, 2015 at 15:50 Comment(0)
I
0

There is a blog post recently here about implementing Push Notifications on Xamarin Forms (well each individual platform because there is no Forms based implementation), using Azure Mobile Services.

http://www.xamarinhelp.com/push-notifications/

Informer answered 18/4, 2015 at 3:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.