-
Notifications
You must be signed in to change notification settings - Fork 585
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
[messaging] Ability to change the notification before it's sent to the system tray #2639
Comments
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight. |
The comparison to the iOS Notification Service Extension is a good one - I think Apple has a fairly elegant solution here in allowing an app to effectively "tamper" with a notification payload before it's handled by the system. I'd love to do exactly the same thing in Android Firebase land - mutate the RemoteMessage before the SDK handles it. I have legacy services that are sending a notification payload with my FCM messages. It's not impossible to change these to data only, but it's costly, as it's a breaking change for existing clients and requires me to spend some political capital on getting the services updated and redeployed with new payload versions - and I have to repeat that per notification type, because nearly all of them are impacted. Allowing a client to intercept the payload and remove the "notification" blob before the SDK handles it would be a godsend. Some extensible method on edit: It looks like this is something that could be handled fairly trivially with just changes to the existing Java code in I can't find docs describing the current SDK behavior for firebase-android-sdk/firebase-messaging/src/main/java/com/google/firebase/messaging/Constants.java Line 155 in b2aed41
I have to assume this is based on historical GCM behavior and how the Google services format the notification before it hits the Firebase SDK client code, so it's not as trivial as letting an app receive a RemoteMessage and then tamper with it. Is there a design reason not to allow an app to opt-out of this behavior? I'm open to potentially creating a PR if we can reach consensus on the approach? |
To spitball a surgical proposed change without more edits on my previous post, it'd basically be:
/**
* Called when determining whether to opt an Intent out of default system tray behavior for a "notification message".
* @return true if this message should fall-through to {@link #onMessageReceived}, false for default system behavior.
*/
public boolean shouldOptOutOfNotificationBehavior(@NonNull RemoteMessage message) { return false; }
unless I am misunderstanding, this would unblock the desired behavior of allowing an app to configure the rendered notification while backgrounded, in an opt-out fashion so existing clients are unaffected by default. I'd love to allow modifying the Intent/Bundle before the SDK handles it, but since there seems to be a bunch of protected GCM keys stuffed into the payload (by the system?) I can see why we may not want to expose that to an application. However, allowing an app to inspect the corresponding |
I've been neck deep in Firebase SDK code for the past couple days and am getting confused. Specifically, these comments on a similar issue from 2018:
They plainly indicate that the behavior we're talking about (display messages getting auto-posted to the system tray with no ability for the app to intervene) is Android platform/OS behavior and not Firebase SDK behavior. But in looking at SDK code and in actually testing this functionality, that does not seem to be correct. Did this change between 2018 (time of comments) and 2020 (time FCM was open sourced and committed to this repo)? Specifically this code is what prevents calling onMessageReceived in the background and instead manually displays a Notification (using standard, public Android APIs - no magic): Line 215 in 21a9f91
I have confirmed this evening that if I override the undocumented I still have my fingers crossed that a kind Googler will tell me where I've lost the plot here and misunderstood how the platform works, because from the Java in this repo and my testing, it's seeming easily fixable - I'll likely try to start work on a PR for an API to work around the current behavior as described in my previous comment. |
Something similar has been discussed and proposed in the past internally and has come up again recently, but we don't have anything new to report at this time. Support for this is mostly a product question that comes down to prioritization, demand for the feature, and some other considerations. |
@gsakakihara PR #3492 aims to provide a tactical new API to unblock this, I'm hoping you or the team can shed some light on whether it's a feasible approach or if I'm missing something (more fundamental on an Android platform level) about why this might be unsupported. |
For anyone following this issue - since the Firebase team is not showing any interest in my PR to tweak the API for this scenario, my app team has been working around this problem with a custom This seems to work just fine despite being an undocumented workaround. I have not received any reports of it causing problems despite being live for a few months. The approach is based on how the SDK's service works under the covers and is something like: // this is inside a custom FirebaseMessagingService which we've hooked up in our AndroidManifest
// 1. excessively document for your team with links to GitHub issues, PRs, StackOverflow, etc
// 2. emphasize that this is a hack which risks breaking in future Firebase SDK updates :(
// 3. dream of an officially supported API for this scenario
@Override
public void handleIntent(Intent intent) {
Bundle extras = intent.getExtras();
// try/catch around this to be defensive; default to super.handleIntent
RemoteMessage message = new RemoteMessage(extras);
RemoteMessage.Notification notificationData = message.getNotification();
// short-circuit if this is not a display notification
if (notificationData == null) { super.handleIntent(intent); return; }
// 1. filter on the notification however you want using the data payload, clickAction, etc
// 2. manually display notifications that match your filter, and then return
// 3. fall-through to this for everything else...
super.handleIntent(intent);
} |
thanks @sfuqua for suggesting this. your approach makes total sense. will use it a starting base |
ping any news? |
@sfuqua Thank you ! It works for me as well |
@sfuqua thank you for this, we were able to implement similar approach overriding the handleIntent from the class that extends private void createNotificationChannel() {
// Same as Firebase SDK default channel name and ids
NotificationChannel channel = new NotificationChannel("fcm_fallback_notification_channel", "Miscellaneous", NotificationManager.IMPORTANCE_HIGH);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
private int getNotificationIcon() {
int iconResId;
iconResId = getResources().getIdentifier("ic_notification", "drawable", getPackageName());
if (iconResId == 0) {
iconResId = getApplicationInfo().icon;
}
return iconResId;
}
@Override
public void handleIntent(Intent intent) {
Bundle extras = intent.getExtras();
try {
RemoteMessage message = new RemoteMessage(extras);
RemoteMessage.Notification notificationData = message.getNotification();
if (notificationData == null) {
super.handleIntent(intent);
return;
}
Bundle customExtras = new Bundle();
for (Map.Entry<String, String> entry : message.getData().entrySet()) {
customExtras.putString(entry.getKey(), entry.getValue());
}
createNotificationChannel();
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "fcm_fallback_notification_channel")
.setSmallIcon(getNotificationIcon())
.setContentTitle(notificationData.getTitle())
.setContentText(notificationData.getBody())
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_HIGH);
notificationBuilder.getExtras().putBundle("data", customExtras);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Same as tag from Firebase SDK is built
notificationManager.notify("FCM-Notification:" + SystemClock.uptimeMillis(), 0, notificationBuilder.build());
} catch (Exception e) {
super.handleIntent(intent);
}
} And in Notifee module: private List<Bundle> fetchNotificationData(List<Bundle> aBundleList) {
NotificationManager notificationManager = (NotificationManager) getReactApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
StatusBarNotification[] activeNotifications = notificationManager.getActiveNotifications();
for (StatusBarNotification sbn : activeNotifications) {
Notification notification = sbn.getNotification();
Bundle extras = notification.extras;
if (extras != null) {
Bundle data = extras.getBundle("data");
if (data != null) {
for (Bundle originalBundle : aBundleList) {
Bundle originalNotificationBundle = originalBundle.getBundle("notification");
if (originalNotificationBundle != null && originalNotificationBundle.getString("id").equals(String.valueOf(sbn.getId()))) {
originalNotificationBundle.putBundle("data", data);
}
}
}
}
}
}
return aBundleList;
}
@ReactMethod
public void getDisplayedNotifications(Promise promise) {
Notifee.getInstance()
.getDisplayedNotifications(
(e, aBundleList) -> NotifeeReactUtils.promiseResolver(promise, e, fetchNotificationData(aBundleList)));
} |
What feature would you like to see?
Hi, is there anything on the roadmap to provide the ability to customise the notification before it's sent to the system tray. I'm a maintainer of notifee, and we recently added support to do this with iOS, we'd love to be able to do it with Android too.
Currently, the only option there seems to be is to send data messages. A couple of issues with this method are that you need to send two different types of messages based on whether the device is android or iOS, and having to keep track of which platform the token is registered with.
In addition to platform-dependent code, there's also different background restrictions for data-only vs notification messages (ref here).
On iOS, you can alter the notification using a Notification Service Extension where you have a brief amount of time to modify the notification before it's displayed to the user. in addition to the flexibility that
message.apns.aps
object offers where you can add a categoryId, sound, threadId e.t.c.One possible solution could be to allow FirebaseMessagingService.onMessageReceived to be called for all app states, maybe a check somehow to see if the app does supports this, if not, default to handling with FCM? If this was possible, the one issue I do see with this, is that it could clash with other libraries like
FlutterFire
andreact-native-firebase
as you can only have one FirebaseMessagingService per app.Another solution, which I think seems way easier and less intrusive is to have a separate service, like how iOS works, to alter the notification and nothing else.
I can see how this could be a big f/r, and maybe there's reasons that it's not been done?
The other possible solution is to expand the predefined set of user-visible keys for message.android, to include
group
and otherNotificationCompat
properties where possible.To summarise, the two pain points I think there is at the moment with Android is:
How would you use it?
Tell us how you'd use this feature in your app.
This feature would allow users of this library to send notification messages and customise the notification before it's finally sent to the system tray.
The text was updated successfully, but these errors were encountered: