shinyorg / shiny

.NET Framework for Backgrounding & Device Hardware Services (iOS, Android, & Catalyst)
https://shinylib.net
MIT License
1.43k stars 227 forks source link

[Bug]: Notification doesn't appear when the app is running #1464

Closed IeuanWalker closed 4 months ago

IeuanWalker commented 4 months ago

Component/Nuget

Push - Native (Shiny.Push)

What operating system(s) are effected?

Version(s) of Operation Systems

Android 13

Hosting Model

Steps To Reproduce

I've followed the docs and the device is receiving the notifications.

When the app is closed, the device displays the notification. But if the app is running, the OnRecieved delegate method is hit and runs fine, but the device never displays the notifications.

So not sure if I'm missing something in the OnReceived method to trigger the notification to display, or if Shiny is expecting a different payload in the notification.

This is the OnRecieved method -

    public Task OnReceived(PushNotification notification)
    {
        WeakReferenceMessenger.Default.Send(new NotificationReceivedMessage(true));

        if (notification.Data.TryGetValue("NotificationId", out string? notificationId) && int.TryParse(notificationId, out int actualNotificationId))
        {
            _ = Task.Run(() => UpdateBackend(actualNotificationId));
        }

        async Task UpdateBackend(int notificationId)
        {
            try
            {
                string? registrationId = await SecureStorage.Default.GetAsync("pushNotificationRegistrationId");
                string? fcmId = await SecureStorage.Default.GetAsync("fcmToken");

                if (registrationId is not null && fcmId is not null)
                {
                    await _pushNotificationApiService.NotificationReceived(new NotificationReceivedRequestModel
                    {
                        NotificationId = notificationId,
                        RegistrationId = registrationId,
                        FcmId = fcmId
                    });
                }
            }
            catch (Exception)
            {
                // intentionally left blank
            }
        }

        return Task.CompletedTask;
    }

I'm using the Firebase official NuGet package in the backend - https://www.nuget.org/packages/FirebaseAdmin. The backend code is pretty basic -

Message message = new()
{
    Notification = new Notification
    {
        Title = notification.Title,
        Body = notification.Message
    },
    Token = notification.Registration.FcmId,
    Data = data
};

notification.FcmId = await FirebaseMessaging.DefaultInstance.SendAsync(message);

Expected Behavior

Notification to be displayed

Actual Behavior

Notification not displayed

Exception or Log output

No response

Code Sample

No response

Code of Conduct

aritchie commented 4 months ago

That's not a bug. That's how the platforms behave out of the box.

There are tools in Shiny.Push you can use to go around this though. https://github.com/shinyorg/pushtester/blob/main/PushEndToEnd/Delegates/MyPushDelegate.cs has what you are looking for.

IeuanWalker commented 4 months ago

Thanks @aritchie, got it working.

Our current prod app uses azure notification hub, and we are planning to migrate to a new solution. Did a proof of concept using firebase, both handled displaying the notification automatically, so wasn't something I was aware of.

After listening to your notification podcast on Gone Mobile, you convinced me to go fully native on each platform, keep up the great work :)

IeuanWalker commented 4 months ago

@aritchie is there a cleaner way to do this?

In the source code you linked, you have this -

#if ANDROID
    ((AndroidPushNotification)notification).SendDefault(100);
#endif

I found that the notification remains in the user's notification list after they tap it unless you set the AutoCancel flag. So for now I've added this to my OnReceived method -

#if ANDROID
    AndroidPushNotification androidNotification = (AndroidPushNotification)notification;

    Android.App.Notification nativeNotification = androidNotification.CreateBuilder().Build();
    nativeNotification.Flags = NotificationFlags.AutoCancel;
    using NotificationManager service = androidNotification.Platform.GetSystemService<NotificationManager>(Context.NotificationService);
    service.Notify(100, nativeNotification);
#endif

This also works -

#if ANDROID
    AndroidPushNotification androidNotification = (AndroidPushNotification)notification;

    NotificationCompat.Builder builder = androidNotification.CreateBuilder();
    builder.SetAutoCancel(true);

    using NotificationManager service = androidNotification.Platform.GetSystemService<NotificationManager>(Context.NotificationService);
    service.Notify(100, builder.Build());
#endif