Azure / azure-notificationhubs-xamarin

Azure Notification Hubs Sample for Xamarin Forms
MIT License
35 stars 23 forks source link

[BUG] Xamarin Forms Android Push Notifications not received when app has been swiped closed #57

Open MelissaW990 opened 3 years ago

MelissaW990 commented 3 years ago

Describe the bug I have had push notifications configured in my Xamarin Forms app for the last year and this week decided to update to the new SDK, although I am having issues with Xamarin Android.

I have tried to copy the documentation here: https://docs.microsoft.com/en-us/azure/notification-hubs/xamarin-notification-hubs-push-notifications-android-gcm and believe I am 90% of the way there as my app will happily receive push notifications when it is in the foreground and background, but my app cannot receive push notifications when it has been swiped closed, (not force shut). I know this is 100% not a device issue as I tested the old configuration using the FirebaseService class my device receives the push notification as expected. I am also using 'Data' notifications.

Exception or Stack Trace Error (10186) / AndroidRuntime: FATAL EXCEPTION: Firebase-Messaging-Intent-Handle Error (10186) / AndroidRuntime: Process: com.mycompany.AndroidNotificationTest, PID: 10186 Error (10186) / AndroidRuntime: java.lang.NullPointerException: Attempt to invoke interface method 'void com.microsoft.windowsazure.messaging.notificationhubs.NotificationListener.onPushNotificationReceived(android.content.Context, com.microsoft.windowsazure.messaging.notificationhubs.NotificationMessage)' on a null object reference Error (10186) / AndroidRuntime: at com.microsoft.windowsazure.messaging.notificationhubs.FirebaseReceiver.onMessageReceived(FirebaseReceiver.java:52) Error (10186) / AndroidRuntime: at com.google.firebase.messaging.FirebaseMessagingService.dispatchMessage(com.google.firebase:firebase-messaging@@21.0.1:13) Error (10186) / AndroidRuntime: at com.google.firebase.messaging.FirebaseMessagingService.passMessageIntentToSdk(com.google.firebase:firebase-messaging@@21.0.1:8) Error (10186) / AndroidRuntime: at com.google.firebase.messaging.FirebaseMessagingService.handleMessageIntent(com.google.firebase:firebase-messaging@@21.0.1:3) Error (10186) / AndroidRuntime: at com.google.firebase.messaging.FirebaseMessagingService.handleIntent(com.google.firebase:firebase-messaging@@21.0.1:3) Error (10186) / AndroidRuntime: at com.google.firebase.messaging.EnhancedIntentService.lambda$processIntent$0$EnhancedIntentService(Unknown Source:1) Error (10186) / AndroidRuntime: at com.google.firebase.messaging.EnhancedIntentService$$Lambda$0.run(Unknown Source:6) Error (10186) / AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) Error (10186) / AndroidRuntime: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) Error (10186) / AndroidRuntime: at com.google.android.gms.common.util.concurrent.zza.run(com.google.android.gms:play-services-basement@@17.6.0:2) Error (10186) / AndroidRuntime: at java.lang.Thread.run(Thread.java:919)

To Reproduce

  1. Create Xamarin Forms App Project in Visual Studio for Mac
  2. Follow tutorial: https://docs.microsoft.com/en-us/azure/notification-hubs/xamarin-notification-hubs-push-notifications-android-gcm#set-up-notification-hubs-in-your-project to set up the project
  3. Run Project -
  4. Stop Debugger (kills app)
  5. Reopen App from device
  6. Close App Via Swipe Up
  7. Send data push notification from azure notification hubs: {"data":{"title":"Push Notification", "message":"Hello Android Device."}}
  8. Nothing is received to the device and the error is thrown.

Code Snippet internal class AzureListener : Java.Lang.Object, INotificationListener { public void OnPushNotificationReceived(Context context, INotificationMessage message) { Console.WriteLine($"Message received with title {message.Title} and body {message.Body}"); } }

Expected behavior A data push notification sent when the app has been swiped closed should appear in the notification tray and launch the app when it is tapped.

Setup (please complete the following information):

Additional context Android Manifest may be useful as well. `

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<application android:label="AndroidNotificationTest.Android" android:theme="@style/MainTheme"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

`

kiske1 commented 3 years ago

I bump this one as I have the same issue. The only difference my dev machine is Windows

gianox commented 2 years ago

I bump this one as I have the same issue.

MelissaW990 commented 2 years ago

@kiske1 I see you solved this issue yourself, I have tried running it in release mode and for me it still doesn't work :( a year later and I am still getting the same error message.. Any chance you could share the full code of your azure listener class and the manifest file?

kiske1 commented 2 years ago

Hi @MelissaW990, no problem:

here is the PushNotification Implementation:

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Android.Graphics;
using Android.Support.V4.App;
using WindowsAzure.Messaging.NotificationHubs;
using FreshMvvm;
using StarGolfApp.Droid;
using Xamarin.Essentials;
using StarGolfApp.Services.Interfaces;
using Resource = StarGolfApp.Droid.Resource;
//using Resource = Xamarin.Essentials.Resource;

namespace iMae2021.Droid.Services
{
    public class AndroidPushNotification : IPlatformPushNotification
    {
        private const string PUSH_TAGS = "push_tags";
        private readonly Application _application;
        public AndroidPushNotification(Application application)
        {
            _application = application;
        }
        public async Task AddTag(string tag)
        {
            var tagsStorage = (await SecureStorage.GetAsync(PUSH_TAGS))?.Split(";").ToList() ?? new List<string>();

            if (tagsStorage.Count > 0)
            {
                if (string.IsNullOrEmpty(tagsStorage.First()))
                    tagsStorage = new List<string>();
            }

            NotificationHub.AddTag(tag);
            if
                (!tagsStorage.Contains(tag))
                tagsStorage.Add(tag);

            await SecureStorage.SetAsync(PUSH_TAGS, string.Join(";", tagsStorage));
        }
        public async Task AddTags(string[] tags)
        {
            var tagsStorage = (await SecureStorage.GetAsync(PUSH_TAGS))?.Split(";").ToList() ?? new List<string>();

            if (tagsStorage.Count > 0)
            {
                if (string.IsNullOrEmpty(tagsStorage.First()))
                    tagsStorage = new List<string>();
            }

            foreach (var tag in tags)
            {
                if (!tagsStorage.Contains(tag))
                {
                    tagsStorage.Add(tag);
                }
            }

            NotificationHub.ClearTags();
            NotificationHub.AddTags(tags);

            await SecureStorage.SetAsync(PUSH_TAGS, string.Join(";", tagsStorage));
        }
        public string GetDeviceId()
        {
            return NotificationHub.InstallationId;
        }
        public string GetPushChannel()
        {
            return NotificationHub.PushChannel;
        }
        public async Task Register()
        {
            string CONNECTION_STRING = "Endpoint=sb://myhubconnectionsctring";
            string HUB_NAME = "myhubname";

            try
            {
                NotificationHub.SetListener(new NotificationListener());
                NotificationHub.Start(_application, HUB_NAME, CONNECTION_STRING);

                //Recupero l'elenco dei tags registrati e li aggiorno.
                var tags = (await SecureStorage.GetAsync(PUSH_TAGS))?.Split(";");
                NotificationHub.ClearTags();
                if (tags != null)
                {
                    NotificationHub.AddTags(tags);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        /// <summary>
        /// Confronta i tags nel securestorage con quelli in uso e li aggiorna di conseguenza
        /// </summary>
        /// <returns></returns>
        public async void CheckTags()
        {
            //recupero i tag da utilizzare
            var tags = (await SecureStorage.GetAsync(PUSH_TAGS))?.Split(";")
                .ToList();

            //recupero i tag attivi
            var it = NotificationHub.Tags.Iterator();
            var tagsAttivi = new List<string>();
            while (it.HasNext)
            {
                tagsAttivi.Add(it.Next().ToString());
            }

            //elimino
            var toRemove = new List<string>();
            foreach (var tag in tagsAttivi)
            {
                if (!tags.Contains(tag))
                {
                    //tagsAttivi.Remove(tag);
                    NotificationHub.RemoveTag(tag);
                }
            }

            //todo check??
            tagsAttivi.RemoveAll(x => !tags.Any(x2 => x2 == x));

            //aggiungo
            foreach (var tag in tags)
            {
                if (!tagsAttivi.Contains(tag))
                {
                    tagsAttivi.Add(tag);
                    NotificationHub.AddTag(tag);
                }
            }
        }
        public async Task RemoveTag(string tag)
        {
            var tagsStorage = (await SecureStorage.GetAsync(PUSH_TAGS)).Split(";").ToList();

            tagsStorage.Remove(tag);
            NotificationHub.RemoveTag(tag);

            await SecureStorage.SetAsync(PUSH_TAGS, string.Join(";", tagsStorage));
        }
        public async Task RemoveTags(string[] tags)
        {
            var tagsStorage = (await SecureStorage.GetAsync(PUSH_TAGS)).Split(";").ToList();
            NotificationHub.RemoveTags(tags);

            foreach (var tag in tags)
            {
                tagsStorage.Remove(tag);
            }

            await SecureStorage.SetAsync(PUSH_TAGS, string.Join(";", tagsStorage));
        }
        public void ClearNotificationsBadge()
        {
            //do nothing
        }
        public async Task ClearTags()
        {
            try
            {
                NotificationHub.ClearTags();
                await SecureStorage.SetAsync(PUSH_TAGS, "");
            }
            catch (Exception ex)
            {

            }
        }
    }
    public partial class NotificationListener : Java.Lang.Object, INotificationListener
    {
        private INotificationActionService _notificationActionService;
        private INotificationActionService NotificationActionService
            => _notificationActionService ??= FreshIOC.Container.Resolve<INotificationActionService>();

        void INotificationListener.OnPushNotificationReceived(Context context, INotificationMessage message)
        {
            string titoloNotifica = "XXX App";
            string testoNotifica = "";
            if (message.Data != null)
            {
                if (message.Data.TryGetValue("action", out var messageAction)){
                    NotificationActionService.TriggerAction(messageAction);
                    titoloNotifica = "XXX Notificaton";
                    testoNotifica = message.Body;
                }

            }
            CreateLocalNotification(titoloNotifica, testoNotifica);
        }

        void CreateLocalNotification(string titolo, string testo)
        {
            Context context = Android.App.Application.Context;
            var builder = new NotificationCompat.Builder(context, "location_notification")
                .SetAutoCancel(true)
                .SetContentTitle(titolo)
                .SetContentText(testo)
                .SetSmallIcon(Resource.Drawable.Logo);

            var notificationManager = NotificationManagerCompat.From(context);
            notificationManager.Notify(1000, builder.Build());
        }
    }

}

Please note that there are some helpers functions of mine just to manage the push tags

in the MainActivity.cs, in the OnCreate function I call this function:

`void CreateNotificationChannel()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.O)
            {
                return;
            }

            var channelName = "Notifiche Segreteria";
            var channelDescription = "Notifiche dalla segreteria";

            var channel = new NotificationChannel(CHANNEL_ID, channelName, NotificationImportance.Default)
            {
                Description = channelDescription
            };

            var notificationManager = (NotificationManager)GetSystemService(NotificationService);
            notificationManager.CreateNotificationChannel(channel);
        }`

Here is my AndroidManifes.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.xxx.app" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <application android:label="Star Golf" android:theme="@style/MainTheme"></application>
    <queries>
        <intent>
            <action android:name="android.support.customtabs.action.CustomTabsService" />
        </intent>
    </queries>
</manifest>

If you need something else just ask.