[Bug] Change the title and body of incoming Push Notifications BEFORE showing the banner
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.
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).
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
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.
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 :)
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.
I‘ll have a look at it.
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
}
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.
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
https://github.com/thomasgalliker/Plugin.FirebasePushNotifications/blob/01b5e0128d50e17f1929869cc5f418cd81199f6f/Plugin.FirebasePushNotifications/Platforms/Android/NotificationBuilder.cs#L171-L175
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?
Implement the service as descriped above. Then manipulate the data parameter: data["body"]="my value"
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.
@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.
@OvrBtn It should work. Keep in mind that you need data only payload: https://firebase.google.com/docs/cloud-messaging/concept-options?hl=de
@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?
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.
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.
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)
And this is what I receive on device: (Title is constant string and body is some data from local databse)
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);
}
}
I would love to have something like OnBeforeReceived event where I could modify the data before it is handled by the plugin. Also, I can imagine some helper extension methods for the modifications which would set some of common properties like title, body etc. so there are less magic strings. Another thing, it should be possible to cancel the notification from within the event handler with the event object.