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.

starball
  • 20,030
  • 7
  • 43
  • 238
Rohit Vipin Mathews
  • 11,629
  • 15
  • 57
  • 112
  • 1
    This question has been addressed in the official Xamarin Forum, have a look: http://forums.xamarin.com/discussion/20845/how-to-approach-push-notifications-on-xamarin-forms-project – Wizche Mar 14 '15 at 14:07
  • 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. – Rohit Vipin Mathews Mar 16 '15 at 05:46
  • check out HOL On Azure Push notification in Xamrin: https://onedrive.live.com/view.aspx?cid=68D5348E156C9996&resid=68d5348e156c9996%21116456&qt=sharedby&app=Word&authkey=%21AKe8XQrj2IrStV4&wdo=1 – Bhaumik Shah Apr 18 '15 at 05:43

6 Answers6

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 = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
[assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_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 = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_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

Dan Beaulieu
  • 19,406
  • 19
  • 101
  • 135
IdoT
  • 2,831
  • 1
  • 24
  • 35
  • Thank you. I will certainly have a look at it. No I wont need the server side code. – Rohit Vipin Mathews Mar 19 '15 at 10:39
  • 1
    Where are you handling the `DidReceiveRemoteNotification` for IOS? – Rohit Vipin Mathews Mar 19 '15 at 12:09
  • 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) {} – IdoT Mar 19 '15 at 12:38
  • You have broadcast receiver for android that is why i was curious – Rohit Vipin Mathews Mar 19 '15 at 13:41
  • 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. – IdoT Mar 19 '15 at 14:29
  • Hi, @IdoT , 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. – Fernando Pardo Apr 17 '15 at 13:11
  • Yes you can! You can check the sample code here: https://github.com/Redth/PushSharp/blob/master/PushSharp.Sample/Program.cs where you can register to a notification 'OnNotificationSent' and others that indicate it failed. – IdoT Apr 17 '15 at 13:17
  • Yes, sorry I just found OnNotificationFailed 1 minute ago and was going to put here =) thank you for your help! – Fernando Pardo Apr 17 '15 at 13:20
  • @IdoT 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. – Davide Quaglio Apr 21 '15 at 09:04
  • 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. – IdoT Apr 21 '15 at 09:07
  • @DQuaglio - See the last part of [my answer](http://stackoverflow.com/a/29126946/1155650), I have mentioned what I used. – Rohit Vipin Mathews Jun 26 '15 at 07:39
  • @IdoT your Github link for a client is not working. So could you please send me link for both client and server side using Pushsharp – Parth Savadiya Feb 02 '17 at 15:09
  • @pArthsavadiya - please check the reference, you can find in the main page all relevant samples – IdoT Feb 02 '17 at 15:17
  • Actually I dont find IPushNotificationService interface. Where do I find that ? – Anik Saha Jul 16 '17 at 18:45
  • @AnikSaha - this is described in step #1, you need to create it. – IdoT Jul 17 '17 at 07:19
  • how do you implement step 2) ?? – E.Rawrdríguez.Ophanim Jun 27 '18 at 23:04
  • it's detailed in step 3d for Android, and step 4b for iOS – IdoT Jun 28 '18 at 08:12
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

Rohit Vipin Mathews
  • 11,629
  • 15
  • 57
  • 112
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

Xamtastic
  • 105
  • 1
  • 9
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.

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

JordanMazurke
  • 1,103
  • 10
  • 22
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/

Adam
  • 16,089
  • 6
  • 66
  • 109