phonegap / phonegap-plugin-push

Register and receive push notifications
MIT License
1.94k stars 1.91k forks source link

Android - On notification event not triggered when the app is running in background #1774

Open edblokcer opened 6 years ago

edblokcer commented 6 years ago

When my app is running in background on Android, the on notification function isn't triggered when the notification is opened. I read something about sending "content-available": "1", but this still isn't clear.

Expected Behaviour

To registrate the on registration function.

Actual Behaviour

It doesn't registrate the function

Reproduce Scenario (including but not limited to)

Steps to Reproduce

Platform and Version (eg. Android 5.0 or iOS 9.2.1)

Android

(Android) What device vendor (e.g. Samsung, HTC, Sony...)

Samsung

Cordova CLI version and cordova platform version

cordova --version                                    6.5.0
cordova platform version android          android 6.2.2

Plugin version

cordova plugin version | grep phonegap-plugin-push   1.10.4 "PushPlugin"

Sample Push Data Payload

This is the GCM code from the backend

let message = new gcm.Message({
                data: { 
                    "data": 'someData',
                    "content-available": "1",
                },
                notification: {
                    title: "Title notification",
                    body: "Body notification",
                    icon: 'ic_launcher'
                }
            });
          sender.send(message, { registrationTokens: registrationIds }, function (err, response) {// Err handling });

Sample Code that illustrates the problem

push.on('registration', (data) => {
                       // Send to backend
                    });

                    push.on('notification', (data) => {
                        console.log("Notification received", JSON.stringify(data));

                        this.nav.push(SomeComponent, {data: data.additiionalData.data});
                        push.finish(function() {
                            console.log("Finished push");
                        }, function(){
                            console.log("Something went wrong");
                        });

                    });
                    push.on('error', (e) => {
                        console.log("Error", e);
                        console.log(e.message);
                    });

Logs taken while reproducing problem

macdonst commented 6 years ago

@eddyDEVDE did you read the Overview and specifically how you can get the event called when the app is in the background?

As well you should read up on Notification vs Data Payloads. So if you change your server code to:

let message = new gcm.Message({
                data: { 
                    "data": 'someData',
                    "content-available": "1",
                    title: "Title notification",
                    body: "Body notification",
                    icon: 'ic_launcher'
                }
            });
sender.send(message, { registrationTokens: registrationIds }, function (err, response) {// Err handling });

and you should see your notification event fired in the background.

edblokcer commented 6 years ago

@macdonst First of all, thanks for your quick response. The overview explains the workflow, but why is this different on ios? On ios the registration event is triggered while I didn't include the "content- available" parameter first.

I want to push a view in my app when I get a notification, so I wrote the functionality for this in the on registration handler. But now it's triggered twice, when I receive it in the background and when I open the notification. If I send two messages, (one with content available, and one without). How is this able to work when ios also triggers the handler even without the "content- available" parameter.

edblokcer commented 6 years ago

The on registration handler now even gets triggered three times. The last one gives an extra parameter as additionalData: "dismissed":false. Why is that?

I fixed this by giving it an ID, as an earlier answer of you to another issue suggested. That fixes my handling of the notification on android. But I'm still wondering how it's possible to get the notification on background for Android and ios if don't use the notification parameters.

macdonst commented 6 years ago

@eddyDEVDE if you want the on('notification) event listener when the app is in the background for Android you set "content-available": "1" in the data part of the push payload.

If you want the on('notification) event listener when the app is in the background for iOS using APNS then you set "content-available": 1 in the aps part of the push payload.

If you want the on('notification) event listener when the app is in the background for iOS using GCM/FCM then you set "content_available": true in the notification part of the push payload.

demym commented 6 years ago

Hi, i have the same problem (i'm using Ionic2 with your plugin);

my node.js server sends content-available: "1", but the notification event is not fired when the app (android) is in background, only when it is in foreground....

i'm sending the content-available: "1" into the data part of the payload.

Thanks in advance

macdonst commented 6 years ago

@demym logs are useful in this case.

StephanBis commented 6 years ago

Having the same problem, even with sending content-available: 1 in de data part. The notifications pop up when the app is active but when the app is in the background/closed the notifications are getting cached and showed to the user when he opens up the app. We've tried many formats and things like adding no-cache, but this did not help.

Edit: we figured out that Android uses Firebase now so we upgraded to 2.0.0-RC4 but this did not solve the problem. This problem occured with version 1.8. and 1.10. and is still present in 2.0.0.

edblokcer commented 6 years ago

I read the Notification vs Data Payloads. documentation a couple of times and did the following.

Ionic App When a registration token is generated I'm saving this in localStorage with the device operating system.

                    push.on('registration', (data) => {
                        let registrationId = data.registrationId;
                        localStorage.setItem('registrationId', registrationId);

                        if (this.platform.platforms().indexOf('ios') > -1) {
                            localStorage.setItem('registrationType', 'ios');
                        } else if (this.platform.platforms().indexOf('android') > -1) {
                            localStorage.setItem('registrationType', 'android');
                        }

                    });

So instead of saving the registration token to a user (in my example), I'm saving it with the device os. This gives me to opportunity to send different Payload Data.

Server side I get all user registration ids when I'm sending my notification. Then I'm creating two different Message objects, one for Android and one for ios. Finally I'm sending the message to the matching device.

            let iosRegistrationIds = result.registrationIds.ios;
            let promises = [];
            let responses = [];
            let id = uuid();

let androidMessage = new gcm.Message({
                data : {
                    "additionalData" : {
                        "data": 'Extra data',
                    },
                    "content-available": "1",
                    "force-start": "1",
                    title: "Push notification title",
                    body: `Push notification body`,
                    icon: 'ic_launcher',
                    surveryID: id,
                },
            });

            let iosMessage = new gcm.Message({
                notification: {
                    "title": "Push notification title",
                    "body": `Push notification body`,
                    icon: 'ic_launcher',
                },
                data : {
                    "additionalData" : {
                        "appointment": 'Extra data',
                    },
                    "content-available": "1",
                    "force-start": "1",
                    surveryID: id,
                },
            });

            // // Actually send the message 
            if (androidRegistrationIds.length > 0) {
                promises.push(new Promise(function(resolve, reject) {
                    sender.send(androidMessage, { registrationTokens: androidRegistrationIds }, function (err, response) {
                        if (err) reject(err);

                        console.log("Android push send", response);
                        resolve(response);
                    });
                }))

            }

            if (iosRegistrationIds.length > 0) {
                promises.push(new Promise(function(resolve, reject) {
                    sender.send(iosMessage, { registrationTokens: iosRegistrationIds }, function (err, response) {
                        if (err) reject(err);

                        console.log("ios push send", response);
                        resolve(response);
                    });
                }));
            }

            return Promise.all(promises);
dec commented 6 years ago

Hello to all,

First of all thanks very very much for this plugin and your work on that. We experiment a very similar or the same issue trying to send notifications for both Android and iOS devices.

We try several things, in fact almost all the possible related documentation we find here in this Github project's pages, however, we can't get it working as expected.

The problem in our case is, if we do not include the "notification" key in the message payload, then the notifications arrives to Android without problems, and the behaviour is the expected (certain app's view is shown to the user) no matter if the application is in the foreground, background or even closed.

However, with that payload the notifications never arrives to iOS devices. In order to reach these devices we start to add the "notification" key in the message payload, and then, yes, certainly the notifications arrives to the iOS devices, and the behaviour is the expected, but the Android behaviour changes.

What happend if we add the "notification" key in the message payload is Android receive the notifications, but, except if the application is in the foreground, the "OnMessageReceived" event is never executed, so we can't shown to the user the appropiate app's view.

We are using here the latest version of the plugin and read the payload related documentation, as well the installation pages, etc. Everything works fine except for this strange behavoiur. I say strange because no matter what we try (content-available, etc.) we can't get it workin on both iOS and Android.

What the problem is now? The point is we start to think in the solution proposed by @eddyDEVDE above, that is, differentiate between platforms and use a payload (without "notification" key) for Android and other payload (with "notification" key) for iOS... but this is certainly tricky since implied several changes in both the app's client and the server side.

Can someone help a bit in this question? Why, if we add the "notification" key to the message payload the Android behaviour changes? Why if we remove the "notification" key they works like a charm in Android but the notifications never arrives to iOS?

We are using the latest plugin version and the FCM Google service to send the notifications.

Thanks very much again for the plugin, thanks for reading this and also thanks for the possible help on this specific issue! If we can add here more information... please, ask for that and we trying to add as quickly as possible.

Thanks again to all!

dec commented 6 years ago

Hello to all,

After several more tests and read the original Google FCM documentation my honest conclusion is the "content-available" flag has no the expected effect. Certainly if we add a "notification" key in addition to a "data" key, the notifications are received in iOS and the behaviour is the expected.

However, using the same payload (with the "notification" key) for Android, no matter if we use the "content-available" flag, we receive the notifications on Android, but the behaviour is not the expected when the application is closed or in the background.

According to the Google FCM documentation I think the data may arrive to the application in form of "intent extras", however, the "OnMessageReceived" or the "on notification" event is never fired. Curiously, if we remove the "notification" key everything works fine in Android, but the notifications never arrives to iOS.

For the moment we decide to wait a little... instead of trying to separate the users by platforms and send a different payload for different users/platforms. We are not sure if finally we must take this way, however, we have some hope that the "content-available" flag can work as expected in the near future.

macdonst commented 6 years ago

@eddyDEVDE for the iOS payload using FCM the content-available setting is a bit different:

let iosMessage = new gcm.Message({
                notification: {
                    "title": "Push notification title",
                    "body": `Push notification body`,
                    icon: 'ic_launcher',
                },
                data : {
                    "additionalData" : {
                        "appointment": 'Extra data',
                    },
                    surveryID: id,
                },
                "content_available": true
            });
macdonst commented 6 years ago

@dec if you add the notification section to the push payload on Android then onMessageReceived is never called and that is why the functionality of the plugin does not work. I've attempted to documented it here: https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/PAYLOAD.md#notification-vs-data-payloads

Please let me know what's not clear in that section so I can improve it.

When you send via GCM/FCM on iOS I'm using a library from Google in order to handle the incoming message so you need the notification section.

Right now, I have no solution for you other than to send two different payloads. One for Android, one for iOS.

dec commented 6 years ago

Hello to all,

Thanks very much for your answer @macdonst. To me this two paragraphs on the payload documentation are contradictory:

When your app is in the foreground any on('notification') handlers you have registered will be called. However if your app is in the background the notification will show up in the system tray. Clicking on the notification in the system tray will start the app but your on('notification') handler will not be called as messages with only notification payloads will not cause the plugins onMessageReceived method to be called.

The above quote specify that if we use only notification payloads this will not cause the plugins onMessageReceived to be called. However, just below we can find this another paragraph:

When your app is in the foreground any on('notification') handlers you have registered will be called. If your app is in the background the notification will show up in the system tray. Clicking on the notification in the system tray will start the app and your on('notification') handler will not be called as messages with only notification payloads will not cause the plugins onMessageReceived method to be called.

In this second paragraph you are refering to a payload that includes "data" in addition to "notification", however, you end the paragraph saying " as messages with only notification payloads will not cause the plugins onMessageReceived method to be called."

Am I correct and that documentation missing something or maybe I am wrong?

Anyway, what I am trying is to use the same payload for both iOS and Android (and possibly more platforms in the future) and not to differenciate between platforms, since this add complexity to the application's code, and, I have some hope in which we can use the same payload.

But certainly all my tests do not ends correctly. If we send a "data" payload, then Android receive the notification without problems and the onMessageReceived event is fired as expected, no matter if the application is in the foreground or the background. However this notifications never arrives to iOS.

If we add a "notification" payload... even if this only include a "title"... then the notification arrives to iOS and Android, but in Android the behaviour is not the expected if the application is in the background: the onMessageReceived is never executed in this case.

According to the Google documentation, if the application is in the background, the "data" payload (if any) is received by the application "intent extras". Maybe the plugin can read such possible "extras" and then execute the onMessageReceived? I don't know...

Anyway thanks very very much for this plugin and your work and support @macdonst.

fredgalvao commented 6 years ago

@dec The desired conclusion from what the docs try to say is that it's impossible to have the same payload work for both Android and iOS at the same time with the current way GCM/FCM handles notification.

You will need to differentiate the platforms when sending notifications from your server, unfortunately.

dec commented 6 years ago

Hello to all,

Thanks for your reply @fredgalvao . I certainly start to think that we must differenciate between Android and iOS payloads... well, at least doing that everything works as expected.

Anyway, I continue thinking there is a mistake (less or greater) in the documentation about the below payload and paragraph:

{ "notification": { "title": "Test Notification", "body": "This offer expires at 11:30 or whatever", "notId": 10 }, "data" : { "surveyID": "ewtawgreg-gragrag-rgarhthgbad" } }

When your app is in the foreground any on('notification') handlers you have registered will be called. If your app is in the background the notification will show up in the system tray. Clicking on the notification in the system tray will start the app and your on('notification') handler will not be called as messages with only notification payloads will not cause the plugins onMessageReceived method to be called.

Note how the paragraph refers to a "only notification payload", but the above payload includes both "data" and "notification". Anyway my english is far to be perfect... so maybe I am confusing and certainly the documentation is clear at this point.

fredgalvao commented 6 years ago

I agree with you on that, @dec. messages with only notification payloads are not the only way the problem happens, but rather any messages with a notification section on the payload.

Would you like to contribute that to the docs as an improvement through a PR yourself?

dec commented 6 years ago

@fredgalvao, honestly probably I am not the right guy to touch any english documentation, due to my lack of knowledge about the english language. I just see something that appear not a contradiction, but a simple mistake. In fact the problem is not with the documentation, but with the mixed "notification" and "data" payloads. Of course I am happy to help when possible, then, if I can do something useful, just tell me! :)

fredgalvao commented 6 years ago

You underestimate your english, @dec. Don't be afraid to do it, if you want to help 😉 We can always review it on the PR and improve if needed.

dec commented 6 years ago

Thanks very much @fredgalvao, but certainly I am not sure how to proceed! I can say that this payload:

{ "notification": { "title": "Test Notification", "body": "This offer expires at 11:30 or whatever", "notId": 10 }, "data" : { "surveyID": "ewtawgreg-gragrag-rgarhthgbad" } }

Do not correspond with the below paragraph (which describe the payload if I am not wrong):

When your app is in the foreground any on('notification') handlers you have registered will be called. If your app is in the background the notification will show up in the system tray. Clicking on the notification in the system tray will start the app and your on('notification') handler will not be called as messages with only notification payloads will not cause the plugins onMessageReceived method to be called.

What must be changed? Honestly I am not sure... I am not sure if we must change the payload or the payload's description... because I am not sure about the author intention. The point is that, again, If I am not wrong... the description do not match with the described payload, because the description said: "only notification payloads"... but the refered payload include some "data" in addition to the "notification"...

I am very sorry, because if I can help I want to do it... but maybe the author must review what they intend to describe... maybe I am completely wrong and the documentation is exactly as must be... Please sorry if this is the case!

EeKay commented 6 years ago

I'm working with @StephanBis with this. We're using Microsoft's Azure Notification Hub with their template mechanism to distribute push notifications. When we watch the device monitor, we see the notification coming in but the message is NULL and the caching kicks in.

Does anyone know if the message tray on Android doesn't show anything if the message property is empty? Sounds plausible IMO.

We're pondering if the switch from GCM to FCM isn't working with the platform specific templates that are generated by Notification Hub. How could we test if the issue lies in our FCM setup or in MS's Notification hub?

I guess its a valid question for this thread since we experience the same symptoms as everyone else..

omgalbert commented 6 years ago

Hi,

Same problem in Android, in iOS works fine, any advance in this issue?

MarsupiL commented 6 years ago

Same issue here, but when sending a simple curl request with all data within the data key, like so:

curl -X POST -H "Authorization: key=AAAA..." -H "Content-Type: application/json" -d '{"to":"evz...","data":{"content_available":"1", "title":"My title","body":"My message","messageId":"56..."}}' "https://fcm.googleapis.com/fcm/send"

I do receive a notification and can get the payload data when the app is in the background but the app does not open on tap anymore. Adding "content-available":"1" inside data or true in the root didn't make any difference. Adding "force-start": "1" also didn't fix it. I tried on different devices and different Android versions using v1.10.5 of this plugin.

Can you confirm that these issues will be fixed and the same payload could be used on both platforms with v2 of this plugin using FCM?

StephanBis commented 6 years ago

Is there any update regarding this issue?

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

StephanBis commented 5 years ago

Bump to keep this issue alive.. is this project still being maintained?

fredgalvao commented 5 years ago

There are multiple issues on the history of this one, most of them related to the still alive confusion about notification vs data payloads, which have been hopefully cleared on the docs since then. @StephanBis would you mind creating a new issue with up-do-date details on what exactly has remained an issue for you? Also, if you haven't done it yet, please take a renewed look at the docs here.