Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Change the title and body of incoming Push Notifications BEFORE showing the banner #95

Open
Nanopixel opened this issue Dec 4, 2024 · 17 comments
Assignees
Labels
bug Something isn't working

Comments

@Nanopixel
Copy link

I need to change the title and body and sometimes the icon of the incoming Push Notification and THEN show the banner to the user to click on.
I tried it by using a CustomNotificationBuilder implementing INotificationBuilder and also by using a CustomPushNotificationHandler implementing IPushNotificationHandler but nothing worked as expected. (so maybe i should have marked this as a bug?)

Problem with the CustomNotificationBuilder:
The OnNotificationReceived() gets triggered before the banner is shown - so far so good. But when I alter the content, nothing happens and no banner is shown. When I alter the content and make with it a new notification via NotificationManagerCompat the OnNotificationReceived() is triggered again and we have an infinite loop.

Problem with the CustomPushNotificationHandler:
The OnReceived() Handler is never triggered. The banner is shown upon arrival of the push notification as if there is no handler. By the way, the OnOpened() handler works, but that doesn't help.

Please implement a solution similar to the Plugin.FirebasePushNotification by Rendy del Rosario where you could build a custom handler that did the things mentioned above and it worked.

@Nanopixel Nanopixel added the bug Something isn't working label Dec 4, 2024
@thomasgalliker
Copy link
Owner

Ok, interesting. I'm currently bit out of time but I can come back to this when the dust settles.
Can you explain why you want to change the title and the body of the incoming push notification? (I just try to understand that use case).

@Nanopixel
Copy link
Author

Thanks for the quick reply.
We are not sending the message itself but a kind of "code" that is replaced inside the app after reception with the actual title and body in the right language (set in the app) etc.
I'm not excluding I'm using the mentioned Interfaces wrong.. I did not find any documentations of how to create a custom handler/custom builder

@thomasgalliker
Copy link
Owner

Ok, I understand. The thing with the missing documentation is, this project was created because there was nothing ready-to-use when we migrated to .NET MAUI. The CrossGeeks plugin worked nicely for us but it was full of static code and lots of things were bit overly complicated (or I was too stupid to understand it). So, bear with me if not everything is shiny in this library.

So, I take your case as a new use case and try to extend the project accordingly.

  • We have to be able to receive push notifications which contain codes as title/body which can be replaced somewhere before the message is dispatched to the app.
  • The updated title/body should be used in all event handlers and also used to display the notification popup.

@OvrBtn
Copy link

OvrBtn commented Jan 2, 2025

I'd also love an option to handle building the notification by myself.

In my case I get notification info on my server from 3rd party API and the information sent is very limited. I get only the ID of occured event but not any description so I can only send some generic notification.
Good example would be that currently I can only send "You have new message" but if I could handle building the notification from code I'd get more details locally on user's device and I could generate locally a notification saying "You have a message from XYZ".

Btw. I can't get more details on server since public access token to 3rd party API is stored on user's device and private access token is stored on server for safety. By being able to build the notification I could get cached data from SQLite and show more details :)

@OvrBtn
Copy link

OvrBtn commented Jan 3, 2025

Not sure how helpful is this information but something similar is done in here https://github.com/TobiasBuchholz/Plugin.Firebase/blob/development/docs/cloud_messaging.md#overriding-firebasecloudmessagingimplementationnotificationbuilderprovider

I've tried using that package but coulnd't get it to work. Maybe I'm too stupid but I kept getting random error after random error.

@thomasgalliker
Copy link
Owner

I‘ll have a look at it.

@thomasgalliker thomasgalliker self-assigned this Jan 13, 2025
@AlleSchonWeg
Copy link

This can be easily fixed. Implement a custom NotificationBuilder:

public class CustomNotificationBuilder : NotificationBuilder {
		public CustomNotificationBuilder(ILogger<NotificationBuilder> logger, FirebasePushNotificationOptions options) : 		base(logger, options) {
		}
		public override void OnNotificationReceived(IDictionary<string, object> data) {
			//manipulate data
			base.OnNotificationReceived(data);
		}
}

In your MauiProgram.cs register the custom builder:

.UseFirebasePushNotifications(o => {
#if ANDROID
builder.Services.AddSingleton<INotificationBuilder, CustomNotificationBuilder>();
#endif
}

@thomasgalliker
Copy link
Owner

I guess the idea was to manipulate the notification content even before it arrives in CustomNotificationBuilder? The NotificationReceived event would still contain the original notification data.

@OvrBtn
Copy link

OvrBtn commented Jan 13, 2025

I was looking at the code to see if I can maybe implement something myself but sadly I barely have any free time lately so I didn't do much research but taking part of current code as example

var messageBody = this.GetNotificationBody(data);
if (!string.IsNullOrEmpty(messageBody))
{
notificationBuilder.SetContentText(messageBody);
}

I'd like to be able to do something like

var messageBody = this.GetNotificationBody(data); 
//when you send a notification you can specify additional data in json, this would retrieve it
int eventId = GetEventId(messageBody);
//get some personal data from local database about user based on event id,
//return some custom notification text
string newText = GetSomeNewNotificationText(eventId);
 if (!string.IsNullOrEmpty(messageBody)) 
 { 
     notificationBuilder.SetContentText(newText); 
 } 

So it would go like:
Firebase sends notification to device -> application handles it before it's shown and modifies it's final appearance based on additional info sent in notification payload -> application shows modified notification

@AlleSchonWeg does your suggestion allows for this?

@AlleSchonWeg
Copy link

AlleSchonWeg commented Jan 13, 2025

Implement the service as descriped above. Then manipulate the data parameter: data["body"]="my value"

@thomasgalliker
Copy link
Owner

Request for change: Method HandleNotificationReceived in FirebasePushNotificationManagerBase (the platform-agnostic part of the manager class) is the "entry point" for both platforms when a new notification arrives. Well, of course, it first hits some other handlers but HandleNotificationRecived is the first place where both platforms handle the notification in shared code.

What if we place a new just before we do anything else with the notification data. We could even have a sequential pipe of processors. Some contain custom logik like those mentioned by @OvrBtn. The dev could register 0-n INotificationDataProcessor implementations in DI and HandleNotificationReceived would step trough all of them before forwarding the processed data dictionary to 1) the event handler NotificationReceived and 2) the platform-specific notification handlers (e.g. NotificationBuilder on Android).

With this solution we could also eliminate some of the pre-processor logic inside method OnMessageReceived of class PNFirebaseMessagingService.

Let me know what you think.

@OvrBtn
Copy link

OvrBtn commented Jan 21, 2025

@AlleSchonWeg I did try what you proposed and it doesn't work. It seems like the CustomNotificationBuilder is not used and the overrided method is never fired. (Yes I did the builder.Services.AddSingleton<INotificationBuilder, CustomNotificationBuilder>();).

@thomasgalliker I think it sounds good.

@AlleSchonWeg
Copy link

@OvrBtn It should work. Keep in mind that you need data only payload: https://firebase.google.com/docs/cloud-messaging/concept-options?hl=de

@OvrBtn
Copy link

OvrBtn commented Jan 22, 2025

@AlleSchonWeg thanks for reply, sadly it doesn't work.

I have

#if ANDROID
            builder.Services.AddSingleton<INotificationBuilder, CustomNotificationBuilder>();
#endif

            builder
                .UseMauiApp<App>()
                ...
                .UseFirebasePushNotifications()

//I wasn't sure which place is better to register it so I tried before and after UseFirebasePushNotifications
#if ANDROID
            builder.Services.AddSingleton<INotificationBuilder, CustomNotificationBuilder>();
#endif
 public class CustomNotificationBuilder : NotificationBuilder
 {
     public CustomNotificationBuilder(ILogger<NotificationBuilder> logger, FirebasePushNotificationOptions options) : base(logger, options)
     {
     }
     public override void OnNotificationReceived(IDictionary<string, object> data)
     {
         string serialized = JsonSerializer.Serialize(data);
         Log.Warn("TESTING", serialized);
         Preferences.Set("test", serialized);

         data["title"] = "123";
         data["body"] = "123";
         base.OnNotificationReceived(data);
     }
 }

Notification I send

{
  "message":{
    "token":"...",
    "data":{
      "body" : "123"
    }
  }
}

When starting the app I'm checking if the Preference with key "test" is set and it never is.

Is there something more needed to receive the data only notification when app is in background/killed just as normal notifications?

@AlleSchonWeg
Copy link

AlleSchonWeg commented Jan 22, 2025

Hi,
i assume you have everything set up correctly and registered successfully on firebase and allow to receive push. You can check the following things:

  • Set Breakpoint in CustomNotificationBuilder constructor and check if the custom handler is created.
  • Verify the builder.Services collection that your CustomNotificationBuilder is registered
  • override also ShouldHandleNotificationReceived in CustomNotificationBuilder and check if this method is called. If called just return true for testing

One more thing. Sometimes if a debugger is attached it is not working. Try publish the app start the app and then kill the app.

@thomasgalliker
Copy link
Owner

You can implement INotificationBuilder instead of extending NotificationBuilder. Then it‘s 100% in your hands what the logic should do. As @AlleSchonWeg mentioned, the ShouldHandleNotificationReceived method contains sophisticated (complicated:)) logic which decides whether to display the notification message or not.

@OvrBtn
Copy link

OvrBtn commented Jan 22, 2025

Thanks for replies.

i assume you have everything set up correctly and registered successfully on firebase and allow to receive push.

Yep, normal push notifications work correctly.

Set Breakpoint in CustomNotificationBuilder constructor and check if the custom handler is created

The constructor is called.

Try publish the app start the app and then kill the app.

In general I test all the time on physical device because emulator doesn't want to receive notifications.
What I noticed is that when I leave the app and swipe up the background process the notifications work fine but when I go to settings and do force stop notifications stop appearing. But I've tested other plugin (OneSignal) and it works like that too so I'm guessing it's just how android behaves.

override also ShouldHandleNotificationReceived in CustomNotificationBuilder

That's the weird part, I did that and now when I send notifications WITH "notification" property in JSON it works and I can actually modify the notification before it's shown - is that even supposed to work like that 😅? Because I think I've seen some information somewhere that notifications sent with "notification" property are handled automatically by OS so I guess I should not be able to change them.

I tested the data only notifications and they work same as notifications with "notification" property.

As an example:
I send some random notification through firebase site (it works the same when I use API)
Image

And this is what I receive on device: (Title is constant string and body is some data from local databse)

Image

Using this notification builder

  public class CustomNotificationBuilder : NotificationBuilder
  {
      public CustomNotificationBuilder(ILogger<NotificationBuilder> logger, FirebasePushNotificationOptions options) : base(logger, options)
      {

      }

      public override bool ShouldHandleNotificationReceived(IDictionary<string, object> data)
      {
          return true;
      }

      public override void OnNotificationReceived(IDictionary<string, object> data)
      {
          var something = LocalDB.GetAll<SomeClass>();
          data["title"] = "123";
          data["body"] = something [0].CourseName + something [0].CourseUnitId;
          base.OnNotificationReceived(data);
      }
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants