androidcordovapush-notificationphonegapphonegap-plugin-push

Push notifications in Android only work when PhoneGap app is in foreground using node-pushnotifications


In my PhoneGap / Cordova app (using Framework7), I use the <plugin name="phonegap-plugin-push" source="npm" spec="2.3.0" /> to enable push notifications. In the root of the project folder, I have put the google-services.json file. When users log in to my app, the device tokens are registered on my server. Next, I use the node-pushnotifications module to send push notifications to these tokens.

For iOS devices everything works as expected, but for Android it only works when the app is in foreground. When I send push notifications when the app is in background, nothing seems to happen. When the app is opened (brought back to foreground), the notifications that have been missed are showed (more precisely, a pop up message appears that a 'new message has arrived', for each 'missed' push notification as is the expected behavior when the app is in foreground modus).

I have found several related questions and answers on StackOverflow, but they did not solve my issue, but maybe I have missed something in these answers? For example, somewhere it was suggested that you need to specify a message variable. But when I include this in the server code, a parsing error occurs. Also, based on my reading of the instructions of node-pushnotifications module, that should not be necessary.

===

Update 1

When I use the Firebase Console to send push messages to the Android device, everything works as expected (push messages are being shown when the app is in background). I suspect the cause lies in the node-pushnotifications module.

What I have tried so far In the node-pushnotifications module settings, I have:

  1. Set icon to default;
  2. Set sound to default;
  3. Set ID to SenderID (results in failure --> notification not sent);
  4. Set phonegap to false (results in failure --> notification not sent)

===

Update 2

Push notifications are correctly delivered using the node-gcm module when the app is in background (but not when the app is closed).

===

Any help is appreciated to make this work using node-pushnotifications is welcome. I am happy to give more explanations and details when needed.

Code in the app

Based on the code provided at http://macdonst.github.io/push-workshop/

Note. For the SenderID, I used the sender id key generated in/by Firebase.

var message = {
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
        document.getElementById("toggleBtn").addEventListener('click', this.toggle, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
      message.push = PushNotification.init({
           "android": {
             "senderID": "**********"
           },
           "ios": {
             "sound": true,
             "vibration": true,
             "badge": false
           },
           "windows": {}
       });

       message.push.on('registration', function(data) {
           var oldRegId = localStorage.getItem('registrationId');
           if (oldRegId !== data.registrationId) {
               // Save new registration ID
               localStorage.setItem('registrationId', data.registrationId);
               // Post registrationId to your app server as the value has changed
               var uname= window.localStorage.getItem('uname');
               var pwd= window.localStorage.getItem('pwd');
               var devicetoken=localStorage.getItem('registrationId');
               
               var dataString="uname="+uname+"&pwd="+pwd+"&devicetoken="+devicetoken+"&updateregistration=yes";
            
                $.ajax({
                    type:"POST",  
                    url:"******************", data: dataString,
                    crossDomain: true,
                    cache: false, 
                    success:function(data)  
                    {  
                    }  
                });  
   
           }
       });

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

        message.push.on('notification', function(data) {
          console.log('notification event');
            
          app.dialog.alert("You have a new message", function () {
              
                        app.views.main.router.navigate('/messages/');

            });
        
        });
    }
};

message.initialize();

Code on the server

Based on the code provided at https://github.com/appfeel/node-pushnotifications

Note. For the gcm id, I used the server key generated in/by Firebase.

// Step 1: configure push notification settings
var PushNotifications = require('node-pushnotifications')

const settings = {
    gcm: {
       id: '*******',
       phonegap: true, // phonegap compatibility mode, see below (defaults to false)
    },
    apn: {
       token: {
            key: '*******', // optionally: fs.readFileSync('./certs/key.p8')
            keyId: '*******',
            teamId: '*******',
        },
        production: true // true for APN production environment, false for APN sandbox environment,
        //...
    },
    isAlwaysUseFCM: false, // true all messages will be sent through node-gcm (which actually uses FCM)
};
const push = new PushNotifications(settings);

// Step 2 create the notification
const data = {
    title: 'New push notification', // REQUIRED for Android
    topic: '**********', // REQUIRED for iOS (apn and gcm)
    /* The topic of the notification. When using token-based authentication, specify the bundle ID of the app.
     * When using certificate-based authentication, the topic is usually your app's bundle ID.
     * More details can be found under https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns
     */
    body: 'Powered by AppFeel',
    custom: {
        sender: 'AppFeel',
    },
    priority: 'high', // gcm, apn. Supported values are 'high' or 'normal' (gcm). Will be translated to 10 and 5 for apn. Defaults to 'high'
    collapseKey: '', // gcm for android, used as collapseId in apn
    contentAvailable: true, // gcm, apn. node-apn will translate true to 1 as required by apn.
    delayWhileIdle: true, // gcm for android
    restrictedPackageName: '', // gcm for android
    dryRun: false, // gcm for android
    icon: '', // gcm for android
    image: '', // gcm for android
    style: '', // gcm for android
    picture: '', // gcm for android
    tag: '', // gcm for android
    color: '', // gcm for android
    clickAction: '', // gcm for android. In ios, category will be used if not supplied
    locKey: '', // gcm, apn
    locArgs: '', // gcm, apn
    titleLocKey: '', // gcm, apn
    titleLocArgs: '', // gcm, apn
    retries: 1, // gcm, apn
    encoding: '', // apn
    badge: 2, // gcm for ios, apn
    sound: 'ping.aiff', // gcm, apn
    android_channel_id: '', // gcm - Android Channel ID
    notificationCount: 0, // fcm for android. badge can be used for both fcm and apn
    alert: { // apn, will take precedence over title and body
        title: 'title',
        body: 'body'
        // details: https://github.com/node-apn/node-apn/blob/master/doc/notification.markdown#convenience-setters
    },
    silent: false, // apn, will override badge, sound, alert and priority if set to true
    /*
     * A string is also accepted as a payload for alert
     * Your notification won't appear on ios if alert is empty object
     * If alert is an empty string the regular 'title' and 'body' will show in Notification
     */
    // alert: '',
    launchImage: '', // apn and gcm for ios
    action: '', // apn and gcm for ios
    category: '', // apn and gcm for ios
    // mdm: '', // apn and gcm for ios. Use this to send Mobile Device Management commands.
    // https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/MobileDeviceManagementProtocolRef/3-MDM_Protocol/MDM_Protocol.html
    urlArgs: '', // apn and gcm for ios
    truncateAtWordEnd: true, // apn and gcm for ios
    mutableContent: 0, // apn
    threadId: '', // apn
    pushType: undefined, // apn. valid values are 'alert' and 'background' (https://github.com/parse-community/node-apn/blob/master/doc/notification.markdown#notificationpushtype)
    expiry: Math.floor(Date.now() / 1000) + 28 * 86400, // unit is seconds. if both expiry and timeToLive are given, expiry will take precedence
    timeToLive: 28 * 86400,
    headers: [], // wns
    launch: '', // wns
    duration: '', // wns
    consolidationKey: 'my notification', // ADM
};

Solution

  • This is the solution I implemented (currently working and tested on Android 10 and iOS 13.6)

    It can help you to easily shows customized push notifications on background, foreground, when in use, when the phone is locked.

    My configuration in CLI:

    cordova -v
    

    7.1.0

    cordova platforms
    

    android 8.0.0

    ios 4.5.5

    cordova plugins
    

    phonegap-plugin-push 2.3.0 "PushPlugin"

    phonegap-plugin-multidex 1.0.0 "Multidex"

    The JS app code I am using

    document.addEventListener("deviceready", registrarDevice, false);
    
    function registrarDevice() {
        try {
            var push = PushNotification.init({
                android: {
                    senderID: "YOUR_SENDER_ID_FROM_FIREBASE"
                },
                browser: {
                    pushServiceURL: 'http://push.api.phonegap.com/v1/push'
                },
                ios: {
                    alert: "true",
                    badge: "true",
                    sound: "true"
                },
                windows: {}
            });
    
            push.on('registration', function (data) {
                // data.registrationId
                console.log(data);
            });
    
            push.on('notification', function (data) {
                console.log(data);
                // data.message,
                // data.title,
                // data.count,
                // data.sound,
                // data.image,
                // data.additionalData
            });
    
            push.on('error', function (e) {
                // e.message
                console.log(e);
            });
        } catch (err) {
            console.log("Error registrarDevice: ", err.message);
        }
    }
    

    The PHP code on my server

    class FCM {
        function __construct() {
        }
    
        public function send_push_notification($registatoin_ids, $notification, $device_type) {
            $url = 'https://fcm.googleapis.com/fcm/send';
            if($device_type == "Android"){
                $fields = array(
                    'to' => $registatoin_ids,
                    'data' => $notification
                );
            } else {
                $fields = array(
                    'to' => $registatoin_ids,
                    'notification' => $notification
                );
            }
            // Your Firebase Server API Key
            $apikey = "YOUR_FIREBASE_SERVER_API_KEY";
            $headers = array('Authorization:key='.$apikey,'Content-Type:application/json');
            // Open curl connection
            $ch = curl_init();
            // Set the url, number of POST vars, POST data
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
            $result = curl_exec($ch);
            if ($result === FALSE) {
                die('Curl failed: ' . curl_error($ch));
            }
            curl_close($ch);
        }
    }   
    
    
    function notificaciones_android_base($xdevice_destino,$xtitulo,$xtexto){
        $NotificationArray= array();                                                        
        $NotificationArray["body"] = $xtexto;
        $NotificationArray["title"] = $xtitulo;
        $NotificationArray["sound"] = "default";
        $NotificationArray["type"] = 1;
    
        $fcm = new FCM();
        $retresult = $fcm->send_push_notification($xdevice_destino, $NotificationArray, "Android");
    
        return $retresult;
    }
    
    
    $info_test="Greetings folks!, Testing de push message :-)";
    echo "<br>".notificaciones_android_base("YOUR_DEVICE_ID","NotificationTitle2021",$info_test);