phonegap / phonegap-plugin-push

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

Android push.on('notification') event handlers not being called when using FCM #2404

Closed miqmago closed 6 years ago

miqmago commented 6 years ago

Related issue: https://github.com/phonegap/phonegap-plugin-push/issues/1062

Expected Behaviour

Android only, app closed or in background:

Actual Behaviour

Android only, app closed or in background:

Reproduce Scenario (including but not limited to)

Steps to Reproduce

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

android 8.0, android 7.0

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

Cordova CLI version and cordova platform version

cordova 8.0.0
cordova android 7.1.0

Plugin version

phonegap-plugin-push 2.2.3 "PushPlugin"

Sample Push Data Payload

Tried with different combinations, including or not including 'content-available' and/or 'force-start'. Tried also with string, integer or boolean.

{
    "title": "hello",
    "message": "world",
    "additionalData": {
        "force-start": "1",
        "content-available": "1",
    }
}

Sample Code that illustrates the problem

Ionic PushService:

    isPushEnabled() {
        return JSON.parse(localStorage.getItem('isPushEnabled') || 'false');
    }

    init(): Promise<any> {
        if (this.isPushEnabled()) {
            return this.enableDisable(true);
        }

        return Promise.resolve();
    }

    enableDisable(isEnabled): Promise<any> {
        if (isEnabled) {
            return this.enable();
        }
        return this.disable();
    }

    private async enable(): Promise<any> {
        try {
            await this.plt.ready();
            const res = await this.push.hasPermission();
            if (!res.isEnabled) {
                throw new Error('Please enable push notifications to receive them');
            }
            await this.createChannel(res);
            const { registrationId } = await this.subscribeEvents();
            const device = await this.updateRegIdToServer(true, registrationId);
            console.log('server OK', device);
            this.saveRegId(device.regId);
            this.savePushEnabled(true);
            return true;
        } catch (err) {
            console.log('Push enable error: ', err);
            throw err;
        }
    }

    private createChannel(res): Promise<any> {
        console.log('Creating channel');
        // Create a channel (Android O and above).
        // You'll need to provide the id, description and importance properties.
        return this.push.createChannel({
            id: ENV.pushChannel,
            description: 'Push channel',
            // The importance property goes from
            // 1 = Lowest, 2 = Low, 3 = Normal, 4 = High and 5 = Highest.
            importance: 3,
        });
    }

    private subscribeEvents(): Promise<any> {
        const pushObject: PushObject = this.push.init(this.options);
        pushObject.on('notification').subscribe(this.onNotification.bind(this));
        pushObject.on('error').subscribe(this.onError.bind(this));
        return new Promise((resolve, reject) => {
            pushObject.on('registration').subscribe(resolve, reject);
        });
    }

    private onNotification(notification: any) {
        this.notification.next(notification);
        this.showMessage(notification);
    }

app.component.ts:

    constructor(
        public platform: Platform,
        public splashScreen: SplashScreen,
        public screenMsg: ScreenMessagesService,
        private sqliteService: SQLiteService,
        private pushSvc: PushService,
    ) {
        console.log('love is in the air');
        this.initializeApp();
    }

    initializeApp() {
        this.platform.ready()
            .then(() => this.sqliteService.init())
            .catch(err => this.screenMsg.showToast(`Error: ${err.message}`))
            .then(() => this.pushSvc.init())
            .catch(err => this.screenMsg.showToast(`Error: ${err.message}`))
            .then(() => this.splashScreen.hide())
            .catch(err => this.screenMsg.showToast(`Error: ${err.message}`));
    }

Logs taken while reproducing problem

macdonst commented 6 years ago

@miqmago I need to see the push message you send to the device not the one that is received by the device.

miqmago commented 6 years ago

@macdonst Hi Simon, I'm using our node-pushnotifications which uses node-gcm. If there is something that should be modified and you can help, we can adapt node-pushnotifications to fit the needs. Maybe if you can show me how json should be in the request, I would try to achieve it and then adapt the library where needed.

This is the final request:

{
    "method": "POST",
    "headers": {
        "Authorization": "key=..."
    },
    "uri": "https://fcm.googleapis.com/fcm/send",
    "json": {
        "priority": "high",
        "content_available": false,
        "delay_while_idle": false,
        "time_to_live": 2419200,
        "dry_run": false,
        "data": {
            "content-available": "1",
            "force-start": "1",
            "title": "hello",
            "message": "world",
            "icon": "pn_icon.png"
        },
        "notification": {
            "title": "hello",
            "body": "world",
            "icon": "pn_icon.png"
        },
        "registration_ids": [
            "..."
        ]
    },
    "timeout": 180000
}

Other things tried:

        "data": {
            "content-available": "1",
            "force-start": "1",
            "title": "hello",
            "message": "wow",
            "icon": "pn_icon.png"
        },
        "notification": {
            "title": "hello",
            "body": "wow",
            "icon": "pn_icon.png",
            "content-available": "1"
        },

also tried with integers and booleans. Also tried directly with content_available.

macdonst commented 6 years ago

@miqmago whelp, this has been discussed many times on the issues here and documented in this section data vs notification payloads.

Move everything out of notification and it will start to work.

{
    "method": "POST",
    "headers": {
        "Authorization": "key=..."
    },
    "uri": "https://fcm.googleapis.com/fcm/send",
    "json": {
        "priority": "high",
        "content_available": false,
        "delay_while_idle": false,
        "time_to_live": 2419200,
        "dry_run": false,
        "data": {
            "content-available": "1",
            "force-start": "1",
            "title": "hello",
            "message": "world",
            "icon": "pn_icon.png"
        },
        "registration_ids": [
            "..."
        ]
    },
    "timeout": 180000
}
miqmago commented 6 years ago

Ouch... Thanks! I'll adapt our plugin. Maybe I would suggest to improve the title of docs, I was looking everywhere but there (I've read all API and also https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/PAYLOAD.md#user-clicks-on-notification-in-notification-center, wasted many hours testing and testing and looking on internet about this... maybe a small reference there would help, I'm open to contribute)

macdonst commented 6 years ago

@miqmago yup, PR's are always welcome

limoli commented 6 years ago

Same request with the following body: POST https://fcm.googleapis.com/fcm/send

{
    "priority": "high",
    "content_available": false,
    "delay_while_idle": false,
    "time_to_live": 2419200,
    "dry_run": false,
    "data": {
        "content-available": "1",
        "force-start": "1",
        "title": "hello",
        "message": "world"
    },
    "to": "<fcmToken>"

}

RESULT

Notification comes when app has been killed (background case), but when tapped it doesn't execute handler, simply start the app and nothing else. @macdonst @miqmago

lock[bot] commented 5 years ago

This thread has been automatically locked.